@bridge-ds/core 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -773,6 +773,11 @@
773
773
  "pattern": "^https?://",
774
774
  "description": "Preview URL pattern, with the same placeholders as previews.apps: {branch}, {rawBranch}, {pr}, {sha} and {sha7}."
775
775
  },
776
+ "production": {
777
+ "type": "string",
778
+ "pattern": "^https?://[^{}\\s]+$",
779
+ "description": "The product in production, without placeholders, such as https://shop.example.com: Live product in the Bridge toolbar (Before of a code change) and See the product today. A product of this repository may use previews.production.<key> instead."
780
+ },
776
781
  "framework": {
777
782
  "type": "string",
778
783
  "description": "The framework the product is built with: react, vue, angular, svelte, astro or other.",
@@ -1,11 +1,11 @@
1
- import { i as isAuditable, a as auditFile } from "../report-CE-jHAuw.js";
2
- import { A, b, c, C, D, d, F, I, L, e, M, N, f, g, h, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, B, E, G, H, J, K, O, P, Q, R, S, T, U, V, W, X, Y, Z } from "../report-CE-jHAuw.js";
3
- import { p as pieceId, a as proposedTokenPath, t as tokenLabel } from "../prod-branch-CJZwMfVD.js";
1
+ import { i as isAuditable, a as auditFile, l as lineCol, b as blankComments, s as scanCssText } from "../report-CxOwwG_w.js";
2
+ import { A, c, d, C, D, e, F, I, L, f, M, N, g, h, j, k, m, n, o, p, q, r, t, u, v, w, x, y, z, B, E, G, H, J, K, O, P, Q, R, S, T, U, V, W, X, Y, Z } from "../report-CxOwwG_w.js";
3
+ import { p as pieceId, a as proposedTokenPath, t as tokenLabel } from "../prod-branch-JL5tmL6u.js";
4
4
  const lines = (text) => text.split(/\r?\n/);
5
5
  function lineBag(text) {
6
6
  const bag = /* @__PURE__ */ new Map();
7
- for (const l2 of text ? lines(text) : []) {
8
- const k2 = l2.trim();
7
+ for (const l of text ? lines(text) : []) {
8
+ const k2 = l.trim();
9
9
  if (k2) bag.set(k2, (bag.get(k2) ?? 0) + 1);
10
10
  }
11
11
  return bag;
@@ -71,9 +71,9 @@ function aspectOf(values) {
71
71
  return "style";
72
72
  }
73
73
  const norm = (f2) => f2.category === "color" ? f2.value.toLowerCase() : f2.value.replace(/\s+/g, "");
74
- function nearestOf(s2) {
75
- if (!s2) return void 0;
76
- return { token: s2.token, cssVar: s2.cssVar, value: s2.value, level: s2.level, label: tokenLabel(s2.token) };
74
+ function nearestOf(s) {
75
+ if (!s) return void 0;
76
+ return { token: s.token, cssVar: s.cssVar, value: s.value, level: s.level, label: tokenLabel(s.token) };
77
77
  }
78
78
  const TOKEN_TYPE = { color: "color", duration: "duration", "font-weight": "fontWeight", "line-height": "number", "z-index": "number", "font-family": "fontFamily", shadow: "shadow", easing: "cubicBezier" };
79
79
  const valueTitle = (category, value) => {
@@ -81,6 +81,105 @@ const valueTitle = (category, value) => {
81
81
  const word = { color: "color", radius: "radius", spacing: "spacing", size: "size", "font-size": "text size", "font-weight": "font weight", "line-height": "line height", shadow: "shadow", duration: "duration", easing: "easing", "z-index": "layer", "border-width": "border width", "font-family": "font" };
82
82
  return `New ${word[category] ?? category} ${v2}`;
83
83
  };
84
+ const LOOK = /^(color|fill|stroke(-width)?|background(-[\w-]+)?|border(-[\w-]+)?|outline(-[\w-]+)?|box-shadow|font(-[\w-]+)?|line-height|letter-spacing|text-(decoration|transform|shadow)(-[\w-]+)?|(min-|max-)?(width|height|inline-size|block-size)|padding(-[\w-]+)?|opacity|filter|--[\w-]+)$/;
85
+ const STYLE_CATEGORY = [
86
+ [/radius/, "radius"],
87
+ [/^border(-[\w-]+)?-width$|^stroke-width$/, "border-width"],
88
+ [/^(color|fill|stroke)$|color$|^background|^border|^outline/, "color"],
89
+ [/^padding/, "spacing"],
90
+ [/width|height|-size$/, "size"],
91
+ [/^font-size$/, "font-size"],
92
+ [/^font-weight$/, "font-weight"],
93
+ [/^line-height$/, "line-height"],
94
+ [/shadow/, "shadow"]
95
+ ];
96
+ const styleCategory = (property) => STYLE_CATEGORY.find(([re]) => re.test(property))?.[1] ?? "style";
97
+ const STATE_WORDS = [
98
+ [/^(aria|data)-pressed$/, "pressed"],
99
+ [/^(aria|data)-selected$/, "selected"],
100
+ [/^(aria|data)-checked$|^checked$/, "checked"],
101
+ [/^aria-expanded$|^data-(open|popup-open|panel-open)$|^open$/, "open"],
102
+ [/^(aria|data)-disabled$|^disabled$/, "disabled"],
103
+ [/^(aria|data)-invalid$|^invalid$|^user-invalid$/, "invalid"],
104
+ [/^(aria|data)-current$/, "current"],
105
+ [/^data-loading$|^aria-busy$/, "loading"],
106
+ [/^data-highlighted$/, "highlighted"],
107
+ [/^hover$|^data-hover(ed)?$/, "hovered"],
108
+ [/^focus(-visible|-within)?$|^data-focus(ed|-visible)?$/, "focused"],
109
+ [/^active$|^data-active$/, "active"]
110
+ ];
111
+ function selectorState(compound) {
112
+ const words = [];
113
+ const add = (w2) => words.includes(w2) ? void 0 : words.push(w2);
114
+ for (const m2 of compound.matchAll(/\[\s*([\w-]+)\s*(?:[~|^$*]?=\s*(['"]?)([^'"\]]*)\2)?\s*\]/g)) {
115
+ const name = m2[1].toLowerCase();
116
+ const value = m2[3]?.toLowerCase();
117
+ if (name === "class" || name === "id" || name === "href" || name === "type" || name === "role") continue;
118
+ if (name === "data-state" && value) {
119
+ add(value);
120
+ continue;
121
+ }
122
+ const word = STATE_WORDS.find(([re]) => re.test(name))?.[1] ?? (name.startsWith("data-") || name.startsWith("aria-") ? name.replace(/^(data|aria)-/, "").replace(/-/g, " ") : void 0);
123
+ if (word) add(value === "false" ? `not ${word}` : word);
124
+ }
125
+ for (const m2 of compound.matchAll(/:([\w-]+)/g)) {
126
+ const word = STATE_WORDS.find(([re]) => re.test(m2[1].toLowerCase()))?.[1];
127
+ if (word) add(word);
128
+ }
129
+ return words.length ? words.join(" and ") : void 0;
130
+ }
131
+ function selectorCompounds(selector) {
132
+ const out = [];
133
+ let cur = "";
134
+ let depth = 0;
135
+ for (const c2 of selector) {
136
+ if (c2 === "[" || c2 === "(") depth++;
137
+ else if (c2 === "]" || c2 === ")") depth--;
138
+ if (depth === 0 && /[\s>+~]/.test(c2)) {
139
+ if (cur.trim()) out.push(cur.trim());
140
+ cur = "";
141
+ continue;
142
+ }
143
+ cur += c2;
144
+ }
145
+ if (cur.trim()) out.push(cur.trim());
146
+ return out;
147
+ }
148
+ const splitTop = (selector) => {
149
+ const out = [];
150
+ let depth = 0;
151
+ let cur = "";
152
+ for (const c2 of selector) {
153
+ if (c2 === "(" || c2 === "[") depth++;
154
+ else if (c2 === ")" || c2 === "]") depth--;
155
+ if (c2 === "," && depth === 0) {
156
+ out.push(cur);
157
+ cur = "";
158
+ } else cur += c2;
159
+ }
160
+ out.push(cur);
161
+ return out.map((x2) => x2.trim()).filter(Boolean);
162
+ };
163
+ function partOf(compound) {
164
+ if (!compound) return void 0;
165
+ const tag = /^([a-z][\w-]*)/i.exec(compound)?.[1]?.toLowerCase();
166
+ if (tag && ["svg", "path", "use", "g", "circle", "img"].includes(tag)) return "icon";
167
+ const cls = /\.([A-Za-z_][\w-]*)/.exec(compound)?.[1];
168
+ if (cls) return nameFromClass(cls);
169
+ return tag;
170
+ }
171
+ function branchLines(before, after) {
172
+ const bag = lineBag(before);
173
+ const out = /* @__PURE__ */ new Set();
174
+ lines(after).forEach((l, i) => {
175
+ const k2 = l.trim();
176
+ if (!k2) return;
177
+ const n2 = bag.get(k2) ?? 0;
178
+ if (n2 > 0) bag.set(k2, n2 - 1);
179
+ else out.add(i + 1);
180
+ });
181
+ return out;
182
+ }
84
183
  function missingPieces(input) {
85
184
  const { ds } = input;
86
185
  const files = input.files.filter((f2) => f2.after !== null && isAuditable(f2.path));
@@ -88,13 +187,44 @@ function missingPieces(input) {
88
187
  const variants = /* @__PURE__ */ new Map();
89
188
  const tokens = /* @__PURE__ */ new Map();
90
189
  const foreign = /* @__PURE__ */ new Map();
190
+ const classUseCache = /* @__PURE__ */ new Map();
91
191
  const classUse = (cls) => {
92
- const re = new RegExp(`<([A-Z][\\w.]*)\\b[^<]*?styles(?:\\.${cls}\\b|\\[['"]${cls}['"]\\])`);
192
+ if (classUseCache.has(cls)) return classUseCache.get(cls);
193
+ const c2 = cls.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
194
+ const re = new RegExp(`<([A-Z][\\w.]*)\\b[^<]*?(?:styles(?:\\.${c2}\\b|\\[['"]${c2}['"]\\])|className=(?:"[^"]*?|'[^']*?|\\{[^}]*?["'\`\\s])(?<![\\w-])${c2}(?![\\w-]))`, "g");
195
+ let found = null;
93
196
  for (const f2 of files) {
94
- const m2 = re.exec(f2.after);
95
- if (m2 && ds.components.has(m2[1].split(".")[0])) return { component: m2[1].split(".")[0], text: childText(f2.after, m2.index) };
197
+ for (const m2 of f2.after.matchAll(re)) {
198
+ const root = m2[1].split(".")[0];
199
+ if (ds.components.has(root)) {
200
+ found = { component: root, text: childText(f2.after, m2.index) };
201
+ break;
202
+ }
203
+ }
204
+ if (found) break;
96
205
  }
97
- return null;
206
+ classUseCache.set(cls, found);
207
+ return found;
208
+ };
209
+ const restyles = /* @__PURE__ */ new Map();
210
+ const addRestyle = (r2, value, at) => {
211
+ const key = `${r2.component}|${r2.state ?? ""}|${r2.state ? "" : r2.name}`;
212
+ const e2 = restyles.get(key) ?? { ...r2, values: [], hits: [] };
213
+ if (!e2.values.some((v2) => v2.property === value.property && v2.part === value.part && v2.value === value.value)) e2.values.push(value);
214
+ if (!e2.hits.some((h2) => h2.file === at.file && h2.line === at.line)) e2.hits.push(at);
215
+ restyles.set(key, e2);
216
+ };
217
+ const targetOf = (compound) => {
218
+ for (const m2 of [...compound.matchAll(/\.([A-Za-z_][\w-]*)/g)].reverse()) {
219
+ const cls = m2[1];
220
+ const own2 = [...ds.components].find((n2) => n2.toLowerCase() === cls.toLowerCase());
221
+ if (own2) return { component: own2, cls: null };
222
+ const use = classUse(cls);
223
+ if (use) return { component: use.component, cls };
224
+ }
225
+ const attr = /\[\s*class\s*[*^$~|]?=\s*['"]?([\w-]+)/.exec(compound)?.[1];
226
+ const own = attr ? [...ds.components].find((n2) => n2.toLowerCase() === attr.toLowerCase()) : void 0;
227
+ return own ? { component: own, cls: null } : null;
98
228
  };
99
229
  for (const f2 of files) {
100
230
  const after = f2.after;
@@ -118,10 +248,10 @@ function missingPieces(input) {
118
248
  const css = report.lang === "css" || report.lang === "scss" || report.lang === "less";
119
249
  for (const finding of report.findings) {
120
250
  if (!isNew(finding.line)) continue;
121
- const s2 = finding.suggestion;
251
+ const s = finding.suggestion;
122
252
  const at = { file: f2.path, line: finding.line };
123
- if (s2?.level === "exact" || s2?.level === "high" && finding.category !== "color") {
124
- fixes.push({ ...at, what: finding.value, use: s2.token });
253
+ if (s?.level === "exact" || s?.level === "high" && finding.category !== "color") {
254
+ fixes.push({ ...at, what: finding.value, use: s.token });
125
255
  continue;
126
256
  }
127
257
  const offset = offsetOf(after, finding.line, finding.column);
@@ -163,6 +293,81 @@ function missingPieces(input) {
163
293
  t2.hits.push(hit);
164
294
  tokens.set(key, t2);
165
295
  }
296
+ const flagged = new Set(report.findings.map((x2) => x2.line));
297
+ const fresh = branchLines(f2.before, after);
298
+ const posOf = lineCol(after);
299
+ if (css) {
300
+ const clean = blankComments(after, report.lang !== "css");
301
+ for (const d2 of scanCssText(after, report.lang).decls) {
302
+ const property = d2.property.toLowerCase();
303
+ const { line } = posOf(d2.valueStart);
304
+ if (!LOOK.test(property) || flagged.has(line) || !fresh.has(line)) continue;
305
+ const open = clean.lastIndexOf("{", d2.valueStart);
306
+ if (open < 0) continue;
307
+ const prev = Math.max(clean.lastIndexOf("}", open - 1), clean.lastIndexOf(";", open - 1), clean.lastIndexOf("{", open - 1));
308
+ const selector = clean.slice(prev + 1, open).trim().replace(/\s+/g, " ");
309
+ if (!selector || selector.startsWith("@")) continue;
310
+ for (const sel of splitTop(selector)) {
311
+ const parts = selectorCompounds(sel);
312
+ let at = -1;
313
+ let target = null;
314
+ for (let i = parts.length - 1; i >= 0 && !target; i--) {
315
+ target = targetOf(parts[i]);
316
+ if (target) at = i;
317
+ }
318
+ if (!target) continue;
319
+ const state = selectorState(parts[at]);
320
+ const part = at < parts.length - 1 ? partOf(parts[parts.length - 1]) : void 0;
321
+ const name = target.cls ? nameFromClass(target.cls, target.component) : "restyled";
322
+ addRestyle({ component: target.component, state, name, selector: sel }, { category: styleCategory(property), property, value: d2.value.trim(), ...part ? { part } : {} }, { file: f2.path, line });
323
+ break;
324
+ }
325
+ }
326
+ } else {
327
+ for (const m2 of after.matchAll(/<([A-Z][\w.]*)\b/g)) {
328
+ const root = m2[1].split(".")[0];
329
+ if (!ds.components.has(root)) continue;
330
+ const tagStart = m2.index;
331
+ let end = -1;
332
+ let depth = 0;
333
+ for (let j2 = tagStart + 1; j2 < after.length; j2++) {
334
+ const c2 = after[j2];
335
+ if (c2 === "{") depth++;
336
+ else if (c2 === "}") depth--;
337
+ else if (c2 === ">" && depth <= 0 && after[j2 - 1] !== "=") {
338
+ end = j2;
339
+ break;
340
+ }
341
+ }
342
+ if (end < 0) continue;
343
+ const tag = after.slice(tagStart, end);
344
+ const st = /\bstyle=\{/.exec(tag);
345
+ if (!st) continue;
346
+ let d2 = 0;
347
+ let close = -1;
348
+ for (let j2 = st.index + st[0].length - 1; j2 < tag.length; j2++) {
349
+ if (tag[j2] === "{") d2++;
350
+ else if (tag[j2] === "}" && --d2 === 0) {
351
+ close = j2;
352
+ break;
353
+ }
354
+ }
355
+ if (close < 0) continue;
356
+ const expr = tag.slice(st.index + st[0].length, close);
357
+ const cond = /^\s*!?\s*([\w.]+)\s*(?:\?|&&)/.exec(expr)?.[1];
358
+ const prop = cond ? new RegExp(`\\b(pressed|selected|checked|open|disabled|active|invalid|loading)=\\{\\s*${cond.replace(/\./g, "\\.")}\\s*\\}`).exec(tag)?.[1] : void 0;
359
+ const state = prop;
360
+ const cls = /className=\{?["'`]?([\w-]+)/.exec(tag)?.[1];
361
+ const name = childText(after, tagStart)?.toLowerCase() ?? (cls ? nameFromClass(cls, root) : "restyled");
362
+ for (const kv of expr.matchAll(/(?:^|[{,\s])(['"]?)([A-Za-z-]+)\1\s*:\s*(['"`])([^'"`]*)\3/g)) {
363
+ const property = kv[2].replace(/[A-Z]/g, (ch) => `-${ch.toLowerCase()}`);
364
+ const off = tagStart + st.index + st[0].length + kv.index;
365
+ const { line } = posOf(off + kv[0].length - 1);
366
+ if (!LOOK.test(property) || flagged.has(line) || !fresh.has(line)) continue;
367
+ addRestyle({ component: root, state, name, selector: `${root} style` }, { category: styleCategory(property), property, value: kv[4].trim() }, { file: f2.path, line });
368
+ }
369
+ }
370
+ }
166
371
  for (const c2 of report.components) {
167
372
  if (!isNew(c2.line)) continue;
168
373
  if (c2.suggestion) {
@@ -200,6 +405,22 @@ function missingPieces(input) {
200
405
  where: where(v2.hits)
201
406
  });
202
407
  }
408
+ for (const r2 of restyles.values()) {
409
+ const shown = r2.values.map((x2) => `${x2.part ? `${x2.part} ` : ""}${x2.property ?? x2.category} ${x2.value}`).join(", ");
410
+ const title = r2.state ? `${r2.component}: restyled when ${r2.state}` : `${r2.component}: ${r2.name === "restyled" ? "restyled" : `${r2.name} restyle`}`;
411
+ pieces.push({
412
+ id: pieceId([r2.component, r2.state ? `${r2.state} restyle` : r2.name === "restyled" ? "restyle" : `${r2.name} restyle`]),
413
+ kind: "variant",
414
+ title,
415
+ summary: `${r2.component} restyled from outside the library${r2.state ? ` when ${r2.state}` : ""}, tokens only (${shown}). The library ${r2.component} has no ${r2.state ? "look for this state" : "such variant"} yet.`,
416
+ name: r2.state ?? r2.name,
417
+ component: r2.component,
418
+ aspect: r2.state ? "state" : aspectOf(r2.values),
419
+ values: r2.values,
420
+ where: where(r2.hits),
421
+ restyle: { ...r2.state ? { state: r2.state } : {}, selector: r2.selector }
422
+ });
423
+ }
203
424
  for (const [name, e2] of foreign) {
204
425
  pieces.push({
205
426
  id: pieceId(["component", name]),
@@ -232,7 +453,7 @@ function missingPieces(input) {
232
453
  }
233
454
  const order = { variant: 0, component: 1, token: 2 };
234
455
  const seen = /* @__PURE__ */ new Set();
235
- const out = pieces.sort((a, b2) => order[a.kind] - order[b2.kind]).map((p2) => {
456
+ const out = pieces.sort((a, b) => order[a.kind] - order[b.kind]).map((p2) => {
236
457
  let id = p2.id;
237
458
  for (let i = 2; seen.has(id); i++) id = `${p2.id}-${i}`;
238
459
  seen.add(id);
@@ -242,59 +463,61 @@ function missingPieces(input) {
242
463
  }
243
464
  export {
244
465
  A as AUDIT_EXTENSIONS,
245
- b as AUDIT_IGNORE_DIRS,
246
- c as AUDIT_SCHEMA_VERSION,
466
+ c as AUDIT_IGNORE_DIRS,
467
+ d as AUDIT_SCHEMA_VERSION,
247
468
  C as COLOR_LITERAL_RE,
248
469
  D as DEFAULT_NATIVE_ELEMENTS,
249
- d as DesignSystemIndex,
470
+ e as DesignSystemIndex,
250
471
  F as FINDING_CATEGORIES,
251
472
  I as IGNORE_DIRECTIVE,
252
473
  L as LOOKALIKE_ATTRS,
253
- e as LOOKALIKE_CLASSES,
474
+ f as LOOKALIKE_CLASSES,
254
475
  M as MARKUP_EXTENSIONS,
255
476
  N as NAMED_COLORS,
256
- f as applyAllowlist,
257
- g as applyIgnoreComments,
258
- h as auditCssText,
477
+ g as applyAllowlist,
478
+ h as applyIgnoreComments,
479
+ j as auditCssText,
259
480
  auditFile,
260
- j as auditMarkdown,
261
- k as blankComments,
262
- l as buildAuditReport,
481
+ k as auditMarkdown,
482
+ blankComments,
483
+ m as buildAuditReport,
263
484
  childText,
264
- m as classifyModule,
265
- n as classifyTag,
266
- o as compareWithBaseline,
267
- p as cssPropertyOf,
268
- q as deltaE,
269
- r as domRendererComponent,
270
- s as dsEquivalent,
485
+ n as classifyModule,
486
+ o as classifyTag,
487
+ p as compareWithBaseline,
488
+ q as cssPropertyOf,
489
+ r as deltaE,
490
+ t as domRendererComponent,
491
+ u as dsEquivalent,
271
492
  enclosingTag,
272
- t as historyPoint,
273
- u as ignoreDirectives,
274
- v as importsOf,
493
+ v as historyPoint,
494
+ w as ignoreDirectives,
495
+ x as importsOf,
275
496
  isAuditable,
276
- w as isColorLiteral,
277
- x as isNonVisual,
278
- y as isRouterLink,
279
- z as isSafeFix,
280
- B as libraryLabel,
281
- E as lineCol,
282
- G as lookalikesInClassList,
283
- H as markupBlocks,
497
+ y as isColorLiteral,
498
+ z as isNonVisual,
499
+ B as isRouterLink,
500
+ E as isSafeFix,
501
+ G as libraryLabel,
502
+ lineCol,
503
+ H as lookalikesInClassList,
504
+ J as markupBlocks,
284
505
  missingPieces,
285
506
  nameFromClass,
286
- J as nativeEquivalent,
287
- K as packageName,
288
- O as parseAllowlist,
289
- P as parseCssColor,
290
- Q as planCssFixes,
507
+ K as nativeEquivalent,
508
+ O as packageName,
509
+ P as parseAllowlist,
510
+ Q as parseCssColor,
511
+ R as planCssFixes,
291
512
  ruleClasses,
292
- R as scanClassString,
293
- S as scanCode,
294
- T as scanCssText,
513
+ S as scanClassString,
514
+ T as scanCode,
515
+ scanCssText,
295
516
  U as scanCssValue,
296
517
  V as scanJsStyle,
297
518
  W as scanMarkup,
519
+ selectorCompounds,
520
+ selectorState,
298
521
  X as tailwindColor,
299
522
  Y as tailwindFindings,
300
523
  Z as toOklab
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/audit/missing-pieces.ts"],"sourcesContent":["// What a product branch introduces that the library lacks: the adoption audit run on each changed product file at the\n// branch head and at its start (the merge base with main), keeping only what the branch added. Each new hardcoded value\n// or foreign component is then mapped to the nearest library target:\n// - values written on a library component (an inline style, or a CSS module class used on it) form one variant of that\n// component: \"Badge: overdue tone\";\n// - a component from another UI library with no library equivalent is a new component;\n// - any other value is a token the library lacks: \"New color #C2410C\", beside the closest token.\n// Values that equal a token, and components the library already has, are fixes (use the library), not pieces.\n// Pure: file texts in, pieces out. Uses the audit, so the TypeScript compiler (syntax only).\nimport type { LayeredTokenSet } from '../layered';\nimport { pieceId, proposedTokenPath, tokenLabel, type MissingPiece, type PieceAspect, type PieceNearest, type PieceValue } from '../prod-branch';\nimport type { DesignSystemIndex } from './ds-index';\nimport { auditFile, isAuditable } from './report';\nimport type { ComponentFinding, StyleFinding, TokenSuggestion } from './types';\n\nexport interface BranchFile {\n /** Repo-relative path. */\n path: string;\n /** Text at the branch start, null when the branch adds the file. */\n before: string | null;\n /** Text at the branch head, null when the branch removes it. */\n after: string | null;\n}\n\nexport interface MissingPiecesResult {\n pieces: MissingPiece[];\n /** New values equal to a token and new non-library components the library has an equivalent for. */\n fixes: { file: string; line: number; what: string; use: string }[];\n /** Files read. */\n files: number;\n}\n\nconst lines = (text: string) => text.split(/\\r?\\n/);\n\n/** Trimmed lines of a text as a multiset: a finding is new when its line was not there before. */\nfunction lineBag(text: string | null): Map<string, number> {\n const bag = new Map<string, number>();\n for (const l of text ? lines(text) : []) {\n const k = l.trim();\n if (k) bag.set(k, (bag.get(k) ?? 0) + 1);\n }\n return bag;\n}\n\nconst offsetOf = (text: string, line: number, column: number): number => {\n let off = 0;\n const ls = text.split('\\n');\n for (let i = 0; i < line - 1 && i < ls.length; i++) off += ls[i]!.length + 1;\n return off + Math.max(0, column - 1);\n};\n\n/** The JSX element whose opening tag holds `offset` (an inline style value), or null. */\nexport function enclosingTag(text: string, offset: number): { name: string; start: number } | null {\n for (let i = offset - 1; i >= Math.max(0, offset - 4000); i--) {\n if (text[i] !== '<' || !/[A-Za-z]/.test(text[i + 1] ?? '')) continue;\n let depth = 0;\n let closed = false;\n for (let j = i + 1; j < offset; j++) {\n const c = text[j];\n if (c === '{') depth++;\n else if (c === '}') depth--;\n else if (c === '>' && depth <= 0 && text[j - 1] !== '=') {\n closed = true;\n break;\n }\n }\n if (closed) return null;\n const m = /^<([A-Za-z][\\w.]*)/.exec(text.slice(i));\n return m ? { name: m[1]!, start: i } : null;\n }\n return null;\n}\n\n/** The plain text right after an opening tag (`<Badge ...>Overdue</Badge>` gives \"Overdue\"), when short. */\nexport function childText(text: string, tagStart: number): string | null {\n let depth = 0;\n for (let j = tagStart + 1; j < text.length; j++) {\n const c = text[j];\n if (c === '{') depth++;\n else if (c === '}') depth--;\n else if (c === '>' && depth <= 0 && text[j - 1] !== '=') {\n if (text[j - 1] === '/') return null;\n const rest = /^([^<{]*)</.exec(text.slice(j + 1));\n const t = rest?.[1]?.trim() ?? '';\n return t && t.split(/\\s+/).length <= 3 && t.length <= 30 ? t : null;\n }\n }\n return null;\n}\n\n/** The classes of the CSS rule a declaration at `offset` belongs to: `.badge.overdue { color: red }` gives [\"badge\", \"overdue\"]. */\nexport function ruleClasses(text: string, offset: number): string[] {\n const open = text.lastIndexOf('{', offset);\n if (open < 0) return [];\n const prev = Math.max(text.lastIndexOf('}', open), text.lastIndexOf(';', open));\n const selector = text.slice(prev + 1, open);\n return [...selector.matchAll(/\\.([A-Za-z_][\\w-]*)/g)].map((m) => m[1]!);\n}\n\nconst GENERIC = new Set(['status', 'badge', 'tag', 'pill', 'chip', 'style', 'styles', 'root', 'wrapper', 'container', 'item', 'label', 'text', 'color', 'bg', 'is', 'has', 'variant', 'tone', 'custom', 'new']);\n\n/** \"badgeOverdue\" on Badge -> \"overdue\"; \"invoice-status--late\" -> \"late\". */\nexport function nameFromClass(cls: string, component?: string): string {\n const words = cls\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .split(/[\\s_-]+/)\n .map((w) => w.toLowerCase())\n .filter(Boolean);\n const comp = component?.toLowerCase();\n const kept = words.filter((w) => w !== comp && !GENERIC.has(w));\n return kept[kept.length - 1] ?? words[words.length - 1] ?? cls;\n}\n\nconst ASPECT: Record<string, PieceAspect> = { color: 'tone', radius: 'shape', spacing: 'size', size: 'size', 'font-size': 'size', 'line-height': 'size' };\n\nfunction aspectOf(values: PieceValue[]): PieceAspect {\n for (const a of ['tone', 'shape', 'size'] as const) if (values.some((v) => ASPECT[v.category] === a)) return a;\n return 'style';\n}\n\nconst norm = (f: { category: string; value: string }) => (f.category === 'color' ? f.value.toLowerCase() : f.value.replace(/\\s+/g, ''));\n\nfunction nearestOf(s: TokenSuggestion | undefined): PieceNearest | undefined {\n if (!s) return undefined;\n return { token: s.token, cssVar: s.cssVar, value: s.value, level: s.level, label: tokenLabel(s.token) };\n}\n\nconst TOKEN_TYPE: Record<string, string> = { color: 'color', duration: 'duration', 'font-weight': 'fontWeight', 'line-height': 'number', 'z-index': 'number', 'font-family': 'fontFamily', shadow: 'shadow', easing: 'cubicBezier' };\n\nconst valueTitle = (category: string, value: string): string => {\n const v = category === 'color' && /^#[0-9a-f]+$/i.test(value) ? value.toUpperCase() : value;\n const word: Record<string, string> = { color: 'color', radius: 'radius', spacing: 'spacing', size: 'size', 'font-size': 'text size', 'font-weight': 'font weight', 'line-height': 'line height', shadow: 'shadow', duration: 'duration', easing: 'easing', 'z-index': 'layer', 'border-width': 'border width', 'font-family': 'font' };\n return `New ${word[category] ?? category} ${v}`;\n};\n\ninterface Hit {\n file: string;\n line: number;\n finding: StyleFinding;\n}\n\n/**\n * The pieces a product branch adds that the library lacks. `files` are the changed files (only auditable ones are\n * read; pass the product's files). `set` lets token pieces name the token files of the closest token (light and dark).\n */\nexport function missingPieces(input: { files: BranchFile[]; ds: DesignSystemIndex; set?: LayeredTokenSet | undefined }): MissingPiecesResult {\n const { ds } = input;\n const files = input.files.filter((f) => f.after !== null && isAuditable(f.path));\n const fixes: MissingPiecesResult['fixes'] = [];\n const variants = new Map<string, { component: string; name: string; hits: Hit[] }>();\n const tokens = new Map<string, { category: string; value: string; name: string | null; hits: Hit[] }>();\n const foreign = new Map<string, { finding: ComponentFinding; hits: { file: string; line: number }[] }>();\n // Classes of CSS modules: which DS component uses each one, read from every changed file at its head.\n const classUse = (cls: string): { component: string; text: string | null } | null => {\n const re = new RegExp(`<([A-Z][\\\\w.]*)\\\\b[^<]*?styles(?:\\\\.${cls}\\\\b|\\\\[['\"]${cls}['\"]\\\\])`);\n for (const f of files) {\n const m = re.exec(f.after!);\n if (m && ds.components.has(m[1]!.split('.')[0]!)) return { component: m[1]!.split('.')[0]!, text: childText(f.after!, m.index) };\n }\n return null;\n };\n\n for (const f of files) {\n const after = f.after!;\n const report = auditFile(f.path, after, ds);\n const before = lineBag(f.before);\n const afterLines = lines(after);\n // Lines the branch added (a line that was there before is not the branch's doing), counted so repeats stay fair.\n const added = (line: number) => {\n const k = afterLines[line - 1]?.trim() ?? '';\n const n = before.get(k) ?? 0;\n if (n > 0) {\n before.set(k, n - 1);\n return false;\n }\n return true;\n };\n const addedLines = new Map<number, boolean>();\n const isNew = (line: number) => {\n if (!addedLines.has(line)) addedLines.set(line, added(line));\n return addedLines.get(line)!;\n };\n const css = report.lang === 'css' || report.lang === 'scss' || report.lang === 'less';\n for (const finding of report.findings) {\n if (!isNew(finding.line)) continue;\n const s = finding.suggestion;\n const at = { file: f.path, line: finding.line };\n if (s?.level === 'exact' || (s?.level === 'high' && finding.category !== 'color')) {\n fixes.push({ ...at, what: finding.value, use: s.token });\n continue;\n }\n const offset = offsetOf(after, finding.line, finding.column);\n let component: string | null = null;\n let name: string | null = null;\n if (css) {\n const classes = ruleClasses(after, offset);\n for (const cls of [...classes].reverse()) {\n const use = classUse(cls);\n if (use) {\n component = use.component;\n name = nameFromClass(cls, use.component);\n break;\n }\n }\n if (!component && classes.length) name = nameFromClass(classes[classes.length - 1]!);\n } else {\n const tag = enclosingTag(after, offset);\n const root = tag?.name.split('.')[0];\n if (tag && root && ds.components.has(root)) {\n component = root;\n name = childText(after, tag.start);\n const cls = /className=\\{?[\"'`]?([\\w-]+)/.exec(after.slice(tag.start, offset))?.[1];\n if (!name && cls) name = nameFromClass(cls, root);\n }\n }\n const hit: Hit = { ...at, finding };\n if (component) {\n const label = (name ?? 'custom').toLowerCase();\n const key = `${component}|${label}`;\n const v = variants.get(key) ?? { component, name: label, hits: [] };\n v.hits.push(hit);\n variants.set(key, v);\n continue;\n }\n const key = `${finding.category}|${norm(finding)}`;\n const t = tokens.get(key) ?? { category: finding.category, value: finding.value, name, hits: [] };\n if (!t.name && name) t.name = name;\n t.hits.push(hit);\n tokens.set(key, t);\n }\n for (const c of report.components) {\n if (!isNew(c.line)) continue;\n if (c.suggestion) {\n fixes.push({ file: f.path, line: c.line, what: c.name, use: c.suggestion });\n continue;\n }\n if (c.kind !== 'foreign') continue;\n const e = foreign.get(c.name) ?? { finding: c, hits: [] };\n e.hits.push({ file: f.path, line: c.line });\n foreign.set(c.name, e);\n }\n }\n\n const where = (hits: { file: string; line: number }[]) => hits.slice(0, 5).map((h) => ({ file: h.file, line: h.line }));\n const pieces: MissingPiece[] = [];\n for (const v of variants.values()) {\n const values: PieceValue[] = [];\n for (const h of v.hits) {\n const pv = { category: h.finding.category, ...(h.finding.property ? { property: h.finding.property } : {}), value: h.finding.value };\n if (!values.some((x) => x.category === pv.category && x.property === pv.property && norm(x) === norm(pv))) values.push(pv);\n }\n const aspect = aspectOf(values);\n const bg = v.hits.find((h) => /background/.test(h.finding.property ?? ''))?.finding.suggestion;\n const nearest = nearestOf(bg ?? v.hits.find((h) => h.finding.suggestion)?.finding.suggestion);\n const shown = values.map((x) => `${x.property ?? x.category} ${x.value}`).join(', ');\n pieces.push({\n id: pieceId([v.component, v.name, aspect]),\n kind: 'variant',\n title: `${v.component}: ${v.name} ${aspect}`,\n summary: `${v.component} restyled by hand (${shown}).${nearest ? ` ${nearest.level === 'high' ? 'It looks a lot like' : 'The closest in the library is'} ${nearest.label}.` : ''}`,\n name: v.name,\n component: v.component,\n aspect,\n values,\n ...(nearest ? { nearest } : {}),\n where: where(v.hits),\n });\n }\n for (const [name, e] of foreign) {\n pieces.push({\n id: pieceId(['component', name]),\n kind: 'component',\n title: `New component: ${name}`,\n summary: `${name} comes from ${e.finding.library}, and the library has nothing like it.`,\n name,\n component: name.split('.').pop()!,\n values: [],\n where: where(e.hits),\n });\n }\n for (const t of tokens.values()) {\n const nearest = nearestOf(t.hits[0]!.finding.suggestion);\n const pname = t.name ?? `${t.category === 'color' ? 'custom' : t.category}`;\n const path = proposedTokenPath(nearest?.token, pname, t.category);\n const tokenFiles = nearest && input.set ? [...new Set(input.set.defs.filter((d) => d.path === nearest.token).map((d) => d.file))] : [];\n const n = t.hits.length;\n pieces.push({\n id: pieceId([t.category, t.name ?? t.value.replace(/^#/, '')]),\n kind: 'token',\n title: valueTitle(t.category, t.value),\n summary: `Written by hand in ${n === 1 ? 'one place' : `${n} places`}.${nearest ? ` ${nearest.level === 'high' ? 'It looks a lot like' : 'The closest in the library is'} ${nearest.label} (${nearest.value}).` : ' The library has nothing close.'}`,\n name: pname,\n values: [{ category: t.category, ...(t.hits[0]!.finding.property ? { property: t.hits[0]!.finding.property } : {}), value: t.value }],\n ...(nearest ? { nearest } : {}),\n where: where(t.hits),\n token: { path, type: TOKEN_TYPE[t.category] ?? 'dimension', value: t.value, files: tokenFiles },\n });\n }\n // Variants and components first (they read as features), then values; ids stay unique.\n const order: Record<MissingPiece['kind'], number> = { variant: 0, component: 1, token: 2 };\n const seen = new Set<string>();\n const out = pieces\n .sort((a, b) => order[a.kind] - order[b.kind])\n .map((p) => {\n let id = p.id;\n for (let i = 2; seen.has(id); i++) id = `${p.id}-${i}`;\n seen.add(id);\n return id === p.id ? p : { ...p, id };\n });\n return { pieces: out, fixes, files: files.length };\n}\n"],"names":["l","k","j","c","m","t","w","v","f","s","n","key","e","h","x","d","b","p"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/audit/missing-pieces.ts"],"sourcesContent":["// What a product branch introduces that the library lacks: the adoption audit run on each changed product file at the\n// branch head and at its start (the merge base with main), keeping only what the branch added. Each new hardcoded value\n// or foreign component is then mapped to the nearest library target:\n// - values written on a library component (an inline style, or a CSS module class used on it) form one variant of that\n// component: \"Badge: overdue tone\";\n// - a component from another UI library with no library equivalent is a new component;\n// - any other value is a token the library lacks: \"New color #C2410C\", beside the closest token.\n// - a library component restyled from outside with tokens only (a rule on a class it carries, often with a state\n// selector such as [aria-pressed]) is a piece too, named from the state: \"IconButton: restyled when pressed\". It is a\n// look or a state the component lacks, even though no value is hand-picked. Layout around it (margin, position, gap)\n// is not.\n// Values that equal a token, and components the library already has, are fixes (use the library), not pieces.\n// Pure: file texts in, pieces out. Uses the audit, so the TypeScript compiler (syntax only).\nimport type { LayeredTokenSet } from '../layered';\nimport { pieceId, proposedTokenPath, tokenLabel, type MissingPiece, type PieceAspect, type PieceNearest, type PieceValue } from '../prod-branch';\nimport type { DesignSystemIndex } from './ds-index';\nimport { blankComments, lineCol, scanCssText } from './css';\nimport { auditFile, isAuditable } from './report';\nimport type { ComponentFinding, StyleFinding, TokenSuggestion } from './types';\n\nexport interface BranchFile {\n /** Repo-relative path. */\n path: string;\n /** Text at the branch start, null when the branch adds the file. */\n before: string | null;\n /** Text at the branch head, null when the branch removes it. */\n after: string | null;\n}\n\nexport interface MissingPiecesResult {\n pieces: MissingPiece[];\n /** New values equal to a token and new non-library components the library has an equivalent for. */\n fixes: { file: string; line: number; what: string; use: string }[];\n /** Files read. */\n files: number;\n}\n\nconst lines = (text: string) => text.split(/\\r?\\n/);\n\n/** Trimmed lines of a text as a multiset: a finding is new when its line was not there before. */\nfunction lineBag(text: string | null): Map<string, number> {\n const bag = new Map<string, number>();\n for (const l of text ? lines(text) : []) {\n const k = l.trim();\n if (k) bag.set(k, (bag.get(k) ?? 0) + 1);\n }\n return bag;\n}\n\nconst offsetOf = (text: string, line: number, column: number): number => {\n let off = 0;\n const ls = text.split('\\n');\n for (let i = 0; i < line - 1 && i < ls.length; i++) off += ls[i]!.length + 1;\n return off + Math.max(0, column - 1);\n};\n\n/** The JSX element whose opening tag holds `offset` (an inline style value), or null. */\nexport function enclosingTag(text: string, offset: number): { name: string; start: number } | null {\n for (let i = offset - 1; i >= Math.max(0, offset - 4000); i--) {\n if (text[i] !== '<' || !/[A-Za-z]/.test(text[i + 1] ?? '')) continue;\n let depth = 0;\n let closed = false;\n for (let j = i + 1; j < offset; j++) {\n const c = text[j];\n if (c === '{') depth++;\n else if (c === '}') depth--;\n else if (c === '>' && depth <= 0 && text[j - 1] !== '=') {\n closed = true;\n break;\n }\n }\n if (closed) return null;\n const m = /^<([A-Za-z][\\w.]*)/.exec(text.slice(i));\n return m ? { name: m[1]!, start: i } : null;\n }\n return null;\n}\n\n/** The plain text right after an opening tag (`<Badge ...>Overdue</Badge>` gives \"Overdue\"), when short. */\nexport function childText(text: string, tagStart: number): string | null {\n let depth = 0;\n for (let j = tagStart + 1; j < text.length; j++) {\n const c = text[j];\n if (c === '{') depth++;\n else if (c === '}') depth--;\n else if (c === '>' && depth <= 0 && text[j - 1] !== '=') {\n if (text[j - 1] === '/') return null;\n const rest = /^([^<{]*)</.exec(text.slice(j + 1));\n const t = rest?.[1]?.trim() ?? '';\n return t && t.split(/\\s+/).length <= 3 && t.length <= 30 ? t : null;\n }\n }\n return null;\n}\n\n/** The classes of the CSS rule a declaration at `offset` belongs to: `.badge.overdue { color: red }` gives [\"badge\", \"overdue\"]. */\nexport function ruleClasses(text: string, offset: number): string[] {\n const open = text.lastIndexOf('{', offset);\n if (open < 0) return [];\n const prev = Math.max(text.lastIndexOf('}', open), text.lastIndexOf(';', open));\n const selector = text.slice(prev + 1, open);\n return [...selector.matchAll(/\\.([A-Za-z_][\\w-]*)/g)].map((m) => m[1]!);\n}\n\nconst GENERIC = new Set(['status', 'badge', 'tag', 'pill', 'chip', 'style', 'styles', 'root', 'wrapper', 'container', 'item', 'label', 'text', 'color', 'bg', 'is', 'has', 'variant', 'tone', 'custom', 'new']);\n\n/** \"badgeOverdue\" on Badge -> \"overdue\"; \"invoice-status--late\" -> \"late\". */\nexport function nameFromClass(cls: string, component?: string): string {\n const words = cls\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .split(/[\\s_-]+/)\n .map((w) => w.toLowerCase())\n .filter(Boolean);\n const comp = component?.toLowerCase();\n const kept = words.filter((w) => w !== comp && !GENERIC.has(w));\n return kept[kept.length - 1] ?? words[words.length - 1] ?? cls;\n}\n\nconst ASPECT: Record<string, PieceAspect> = { color: 'tone', radius: 'shape', spacing: 'size', size: 'size', 'font-size': 'size', 'line-height': 'size' };\n\nfunction aspectOf(values: PieceValue[]): PieceAspect {\n for (const a of ['tone', 'shape', 'size'] as const) if (values.some((v) => ASPECT[v.category] === a)) return a;\n return 'style';\n}\n\nconst norm = (f: { category: string; value: string }) => (f.category === 'color' ? f.value.toLowerCase() : f.value.replace(/\\s+/g, ''));\n\nfunction nearestOf(s: TokenSuggestion | undefined): PieceNearest | undefined {\n if (!s) return undefined;\n return { token: s.token, cssVar: s.cssVar, value: s.value, level: s.level, label: tokenLabel(s.token) };\n}\n\nconst TOKEN_TYPE: Record<string, string> = { color: 'color', duration: 'duration', 'font-weight': 'fontWeight', 'line-height': 'number', 'z-index': 'number', 'font-family': 'fontFamily', shadow: 'shadow', easing: 'cubicBezier' };\n\nconst valueTitle = (category: string, value: string): string => {\n const v = category === 'color' && /^#[0-9a-f]+$/i.test(value) ? value.toUpperCase() : value;\n const word: Record<string, string> = { color: 'color', radius: 'radius', spacing: 'spacing', size: 'size', 'font-size': 'text size', 'font-weight': 'font weight', 'line-height': 'line height', shadow: 'shadow', duration: 'duration', easing: 'easing', 'z-index': 'layer', 'border-width': 'border width', 'font-family': 'font' };\n return `New ${word[category] ?? category} ${v}`;\n};\n\ninterface Hit {\n file: string;\n line: number;\n finding: StyleFinding;\n}\n\n// ---------------------------------------------------------------------------------------------------------------\n// Restyles: a library component's look changed from outside with tokens only\n\n/** Properties that change how a component looks (its own paint, shape, type and size), not where it sits. */\nconst LOOK = /^(color|fill|stroke(-width)?|background(-[\\w-]+)?|border(-[\\w-]+)?|outline(-[\\w-]+)?|box-shadow|font(-[\\w-]+)?|line-height|letter-spacing|text-(decoration|transform|shadow)(-[\\w-]+)?|(min-|max-)?(width|height|inline-size|block-size)|padding(-[\\w-]+)?|opacity|filter|--[\\w-]+)$/;\n\nconst STYLE_CATEGORY: [RegExp, string][] = [\n [/radius/, 'radius'],\n [/^border(-[\\w-]+)?-width$|^stroke-width$/, 'border-width'],\n [/^(color|fill|stroke)$|color$|^background|^border|^outline/, 'color'],\n [/^padding/, 'spacing'],\n [/width|height|-size$/, 'size'],\n [/^font-size$/, 'font-size'],\n [/^font-weight$/, 'font-weight'],\n [/^line-height$/, 'line-height'],\n [/shadow/, 'shadow'],\n];\nconst styleCategory = (property: string) => STYLE_CATEGORY.find(([re]) => re.test(property))?.[1] ?? 'style';\n\n/** State selectors in plain words: [aria-pressed='true'] and [data-pressed] read \"pressed\". */\nconst STATE_WORDS: [RegExp, string][] = [\n [/^(aria|data)-pressed$/, 'pressed'],\n [/^(aria|data)-selected$/, 'selected'],\n [/^(aria|data)-checked$|^checked$/, 'checked'],\n [/^aria-expanded$|^data-(open|popup-open|panel-open)$|^open$/, 'open'],\n [/^(aria|data)-disabled$|^disabled$/, 'disabled'],\n [/^(aria|data)-invalid$|^invalid$|^user-invalid$/, 'invalid'],\n [/^(aria|data)-current$/, 'current'],\n [/^data-loading$|^aria-busy$/, 'loading'],\n [/^data-highlighted$/, 'highlighted'],\n [/^hover$|^data-hover(ed)?$/, 'hovered'],\n [/^focus(-visible|-within)?$|^data-focus(ed|-visible)?$/, 'focused'],\n [/^active$|^data-active$/, 'active'],\n];\n\n/** The state a compound selector names: `[aria-pressed='true']:hover` gives \"pressed and hovered\". */\nexport function selectorState(compound: string): string | undefined {\n const words: string[] = [];\n const add = (w: string) => (words.includes(w) ? undefined : words.push(w));\n for (const m of compound.matchAll(/\\[\\s*([\\w-]+)\\s*(?:[~|^$*]?=\\s*(['\"]?)([^'\"\\]]*)\\2)?\\s*\\]/g)) {\n const name = m[1]!.toLowerCase();\n const value = m[3]?.toLowerCase();\n if (name === 'class' || name === 'id' || name === 'href' || name === 'type' || name === 'role') continue;\n if (name === 'data-state' && value) {\n add(value);\n continue;\n }\n const word = STATE_WORDS.find(([re]) => re.test(name))?.[1] ?? (name.startsWith('data-') || name.startsWith('aria-') ? name.replace(/^(data|aria)-/, '').replace(/-/g, ' ') : undefined);\n if (word) add(value === 'false' ? `not ${word}` : word);\n }\n for (const m of compound.matchAll(/:([\\w-]+)/g)) {\n const word = STATE_WORDS.find(([re]) => re.test(m[1]!.toLowerCase()))?.[1];\n if (word) add(word);\n }\n return words.length ? words.join(' and ') : undefined;\n}\n\n/** A selector split into its compound parts: `.a[x] > svg` gives [\".a[x]\", \"svg\"]. */\nexport function selectorCompounds(selector: string): string[] {\n const out: string[] = [];\n let cur = '';\n let depth = 0;\n for (const c of selector) {\n if (c === '[' || c === '(') depth++;\n else if (c === ']' || c === ')') depth--;\n if (depth === 0 && /[\\s>+~]/.test(c)) {\n if (cur.trim()) out.push(cur.trim());\n cur = '';\n continue;\n }\n cur += c;\n }\n if (cur.trim()) out.push(cur.trim());\n return out;\n}\n\nconst splitTop = (selector: string): string[] => {\n const out: string[] = [];\n let depth = 0;\n let cur = '';\n for (const c of selector) {\n if (c === '(' || c === '[') depth++;\n else if (c === ')' || c === ']') depth--;\n if (c === ',' && depth === 0) {\n out.push(cur);\n cur = '';\n } else cur += c;\n }\n out.push(cur);\n return out.map((x) => x.trim()).filter(Boolean);\n};\n\n/** The part a descendant compound names: `svg` is the icon, `.label` the label. */\nfunction partOf(compound: string | undefined): string | undefined {\n if (!compound) return undefined;\n const tag = /^([a-z][\\w-]*)/i.exec(compound)?.[1]?.toLowerCase();\n if (tag && ['svg', 'path', 'use', 'g', 'circle', 'img'].includes(tag)) return 'icon';\n const cls = /\\.([A-Za-z_][\\w-]*)/.exec(compound)?.[1];\n if (cls) return nameFromClass(cls);\n return tag;\n}\n\ninterface Restyle {\n component: string;\n state?: string | undefined;\n name: string;\n selector: string;\n values: PieceValue[];\n hits: { file: string; line: number }[];\n}\n\n/** Lines of `after` the branch added (a line that was there before is not the branch's doing), counted so repeats stay fair. */\nfunction branchLines(before: string | null, after: string): Set<number> {\n const bag = lineBag(before);\n const out = new Set<number>();\n lines(after).forEach((l, i) => {\n const k = l.trim();\n if (!k) return;\n const n = bag.get(k) ?? 0;\n if (n > 0) bag.set(k, n - 1);\n else out.add(i + 1);\n });\n return out;\n}\n\n/**\n * The pieces a product branch adds that the library lacks. `files` are the changed files (only auditable ones are\n * read; pass the product's files). `set` lets token pieces name the token files of the closest token (light and dark).\n */\nexport function missingPieces(input: { files: BranchFile[]; ds: DesignSystemIndex; set?: LayeredTokenSet | undefined }): MissingPiecesResult {\n const { ds } = input;\n const files = input.files.filter((f) => f.after !== null && isAuditable(f.path));\n const fixes: MissingPiecesResult['fixes'] = [];\n const variants = new Map<string, { component: string; name: string; hits: Hit[] }>();\n const tokens = new Map<string, { category: string; value: string; name: string | null; hits: Hit[] }>();\n const foreign = new Map<string, { finding: ComponentFinding; hits: { file: string; line: number }[] }>();\n // Classes of CSS modules: which DS component uses each one, read from every changed file at its head.\n // A plain class name counts too (className=\"product-wishlist\", in a template or a clsx call), as long as the\n // opening tag of a library component carries it.\n const classUseCache = new Map<string, { component: string; text: string | null } | null>();\n const classUse = (cls: string): { component: string; text: string | null } | null => {\n if (classUseCache.has(cls)) return classUseCache.get(cls)!;\n const c = cls.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const re = new RegExp(`<([A-Z][\\\\w.]*)\\\\b[^<]*?(?:styles(?:\\\\.${c}\\\\b|\\\\[['\"]${c}['\"]\\\\])|className=(?:\"[^\"]*?|'[^']*?|\\\\{[^}]*?[\"'\\`\\\\s])(?<![\\\\w-])${c}(?![\\\\w-]))`, 'g');\n let found: { component: string; text: string | null } | null = null;\n for (const f of files) {\n for (const m of f.after!.matchAll(re)) {\n const root = m[1]!.split('.')[0]!;\n if (ds.components.has(root)) {\n found = { component: root, text: childText(f.after!, m.index!) };\n break;\n }\n }\n if (found) break;\n }\n classUseCache.set(cls, found);\n return found;\n };\n const restyles = new Map<string, Restyle>();\n const addRestyle = (r: Omit<Restyle, 'values' | 'hits'>, value: PieceValue, at: { file: string; line: number }) => {\n const key = `${r.component}|${r.state ?? ''}|${r.state ? '' : r.name}`;\n const e = restyles.get(key) ?? { ...r, values: [], hits: [] };\n if (!e.values.some((v) => v.property === value.property && v.part === value.part && v.value === value.value)) e.values.push(value);\n if (!e.hits.some((h) => h.file === at.file && h.line === at.line)) e.hits.push(at);\n restyles.set(key, e);\n };\n /** The library component a compound selector targets: a class it carries, or its own name as a class. */\n const targetOf = (compound: string): { component: string; cls: string | null } | null => {\n for (const m of [...compound.matchAll(/\\.([A-Za-z_][\\w-]*)/g)].reverse()) {\n const cls = m[1]!;\n const own = [...ds.components].find((n) => n.toLowerCase() === cls.toLowerCase());\n if (own) return { component: own, cls: null };\n const use = classUse(cls);\n if (use) return { component: use.component, cls };\n }\n const attr = /\\[\\s*class\\s*[*^$~|]?=\\s*['\"]?([\\w-]+)/.exec(compound)?.[1];\n const own = attr ? [...ds.components].find((n) => n.toLowerCase() === attr.toLowerCase()) : undefined;\n return own ? { component: own, cls: null } : null;\n };\n\n for (const f of files) {\n const after = f.after!;\n const report = auditFile(f.path, after, ds);\n const before = lineBag(f.before);\n const afterLines = lines(after);\n // Lines the branch added (a line that was there before is not the branch's doing), counted so repeats stay fair.\n const added = (line: number) => {\n const k = afterLines[line - 1]?.trim() ?? '';\n const n = before.get(k) ?? 0;\n if (n > 0) {\n before.set(k, n - 1);\n return false;\n }\n return true;\n };\n const addedLines = new Map<number, boolean>();\n const isNew = (line: number) => {\n if (!addedLines.has(line)) addedLines.set(line, added(line));\n return addedLines.get(line)!;\n };\n const css = report.lang === 'css' || report.lang === 'scss' || report.lang === 'less';\n for (const finding of report.findings) {\n if (!isNew(finding.line)) continue;\n const s = finding.suggestion;\n const at = { file: f.path, line: finding.line };\n if (s?.level === 'exact' || (s?.level === 'high' && finding.category !== 'color')) {\n fixes.push({ ...at, what: finding.value, use: s.token });\n continue;\n }\n const offset = offsetOf(after, finding.line, finding.column);\n let component: string | null = null;\n let name: string | null = null;\n if (css) {\n const classes = ruleClasses(after, offset);\n for (const cls of [...classes].reverse()) {\n const use = classUse(cls);\n if (use) {\n component = use.component;\n name = nameFromClass(cls, use.component);\n break;\n }\n }\n if (!component && classes.length) name = nameFromClass(classes[classes.length - 1]!);\n } else {\n const tag = enclosingTag(after, offset);\n const root = tag?.name.split('.')[0];\n if (tag && root && ds.components.has(root)) {\n component = root;\n name = childText(after, tag.start);\n const cls = /className=\\{?[\"'`]?([\\w-]+)/.exec(after.slice(tag.start, offset))?.[1];\n if (!name && cls) name = nameFromClass(cls, root);\n }\n }\n const hit: Hit = { ...at, finding };\n if (component) {\n const label = (name ?? 'custom').toLowerCase();\n const key = `${component}|${label}`;\n const v = variants.get(key) ?? { component, name: label, hits: [] };\n v.hits.push(hit);\n variants.set(key, v);\n continue;\n }\n const key = `${finding.category}|${norm(finding)}`;\n const t = tokens.get(key) ?? { category: finding.category, value: finding.value, name, hits: [] };\n if (!t.name && name) t.name = name;\n t.hits.push(hit);\n tokens.set(key, t);\n }\n // Restyles: declarations the branch added that the audit did not flag (tokens, keywords), on a rule that targets a\n // library component, changing its look.\n const flagged = new Set(report.findings.map((x) => x.line));\n const fresh = branchLines(f.before, after);\n const posOf = lineCol(after);\n if (css) {\n const clean = blankComments(after, report.lang !== 'css');\n for (const d of scanCssText(after, report.lang as 'css' | 'scss' | 'less').decls) {\n const property = d.property.toLowerCase();\n const { line } = posOf(d.valueStart);\n if (!LOOK.test(property) || flagged.has(line) || !fresh.has(line)) continue;\n const open = clean.lastIndexOf('{', d.valueStart);\n if (open < 0) continue;\n const prev = Math.max(clean.lastIndexOf('}', open - 1), clean.lastIndexOf(';', open - 1), clean.lastIndexOf('{', open - 1));\n const selector = clean.slice(prev + 1, open).trim().replace(/\\s+/g, ' ');\n if (!selector || selector.startsWith('@')) continue;\n for (const sel of splitTop(selector)) {\n const parts = selectorCompounds(sel);\n let at = -1;\n let target: ReturnType<typeof targetOf> = null;\n for (let i = parts.length - 1; i >= 0 && !target; i--) {\n target = targetOf(parts[i]!);\n if (target) at = i;\n }\n if (!target) continue;\n const state = selectorState(parts[at]!);\n const part = at < parts.length - 1 ? partOf(parts[parts.length - 1]) : undefined;\n const name = target.cls ? nameFromClass(target.cls, target.component) : 'restyled';\n addRestyle({ component: target.component, state, name, selector: sel }, { category: styleCategory(property), property, value: d.value.trim(), ...(part ? { part } : {}) }, { file: f.path, line });\n break;\n }\n }\n } else {\n // An inline style on a library component: style={{ color: 'var(--...)' }}, or style={saved ? {...} : undefined}\n // next to pressed={saved} (that state names it).\n for (const m of after.matchAll(/<([A-Z][\\w.]*)\\b/g)) {\n const root = m[1]!.split('.')[0]!;\n if (!ds.components.has(root)) continue;\n const tagStart = m.index!;\n let end = -1;\n let depth = 0;\n for (let j = tagStart + 1; j < after.length; j++) {\n const c = after[j];\n if (c === '{') depth++;\n else if (c === '}') depth--;\n else if (c === '>' && depth <= 0 && after[j - 1] !== '=') {\n end = j;\n break;\n }\n }\n if (end < 0) continue;\n const tag = after.slice(tagStart, end);\n const st = /\\bstyle=\\{/.exec(tag);\n if (!st) continue;\n let d2 = 0;\n let close = -1;\n for (let j = st.index + st[0].length - 1; j < tag.length; j++) {\n if (tag[j] === '{') d2++;\n else if (tag[j] === '}' && --d2 === 0) {\n close = j;\n break;\n }\n }\n if (close < 0) continue;\n const expr = tag.slice(st.index + st[0].length, close);\n const cond = /^\\s*!?\\s*([\\w.]+)\\s*(?:\\?|&&)/.exec(expr)?.[1];\n const prop = cond ? new RegExp(`\\\\b(pressed|selected|checked|open|disabled|active|invalid|loading)=\\\\{\\\\s*${cond.replace(/\\./g, '\\\\.')}\\\\s*\\\\}`).exec(tag)?.[1] : undefined;\n const state = prop;\n const cls = /className=\\{?[\"'`]?([\\w-]+)/.exec(tag)?.[1];\n const name = childText(after, tagStart)?.toLowerCase() ?? (cls ? nameFromClass(cls, root) : 'restyled');\n for (const kv of expr.matchAll(/(?:^|[{,\\s])(['\"]?)([A-Za-z-]+)\\1\\s*:\\s*(['\"`])([^'\"`]*)\\3/g)) {\n const property = kv[2]!.replace(/[A-Z]/g, (ch) => `-${ch.toLowerCase()}`);\n const off = tagStart + st.index + st[0].length + kv.index!;\n const { line } = posOf(off + kv[0].length - 1);\n if (!LOOK.test(property) || flagged.has(line) || !fresh.has(line)) continue;\n addRestyle({ component: root, state, name, selector: `${root} style` }, { category: styleCategory(property), property, value: kv[4]!.trim() }, { file: f.path, line });\n }\n }\n }\n for (const c of report.components) {\n if (!isNew(c.line)) continue;\n if (c.suggestion) {\n fixes.push({ file: f.path, line: c.line, what: c.name, use: c.suggestion });\n continue;\n }\n if (c.kind !== 'foreign') continue;\n const e = foreign.get(c.name) ?? { finding: c, hits: [] };\n e.hits.push({ file: f.path, line: c.line });\n foreign.set(c.name, e);\n }\n }\n\n const where = (hits: { file: string; line: number }[]) => hits.slice(0, 5).map((h) => ({ file: h.file, line: h.line }));\n const pieces: MissingPiece[] = [];\n for (const v of variants.values()) {\n const values: PieceValue[] = [];\n for (const h of v.hits) {\n const pv = { category: h.finding.category, ...(h.finding.property ? { property: h.finding.property } : {}), value: h.finding.value };\n if (!values.some((x) => x.category === pv.category && x.property === pv.property && norm(x) === norm(pv))) values.push(pv);\n }\n const aspect = aspectOf(values);\n const bg = v.hits.find((h) => /background/.test(h.finding.property ?? ''))?.finding.suggestion;\n const nearest = nearestOf(bg ?? v.hits.find((h) => h.finding.suggestion)?.finding.suggestion);\n const shown = values.map((x) => `${x.property ?? x.category} ${x.value}`).join(', ');\n pieces.push({\n id: pieceId([v.component, v.name, aspect]),\n kind: 'variant',\n title: `${v.component}: ${v.name} ${aspect}`,\n summary: `${v.component} restyled by hand (${shown}).${nearest ? ` ${nearest.level === 'high' ? 'It looks a lot like' : 'The closest in the library is'} ${nearest.label}.` : ''}`,\n name: v.name,\n component: v.component,\n aspect,\n values,\n ...(nearest ? { nearest } : {}),\n where: where(v.hits),\n });\n }\n for (const r of restyles.values()) {\n const shown = r.values.map((x) => `${x.part ? `${x.part} ` : ''}${x.property ?? x.category} ${x.value}`).join(', ');\n const title = r.state ? `${r.component}: restyled when ${r.state}` : `${r.component}: ${r.name === 'restyled' ? 'restyled' : `${r.name} restyle`}`;\n pieces.push({\n id: pieceId([r.component, r.state ? `${r.state} restyle` : r.name === 'restyled' ? 'restyle' : `${r.name} restyle`]),\n kind: 'variant',\n title,\n summary: `${r.component} restyled from outside the library${r.state ? ` when ${r.state}` : ''}, tokens only (${shown}). The library ${r.component} has no ${r.state ? 'look for this state' : 'such variant'} yet.`,\n name: r.state ?? r.name,\n component: r.component,\n aspect: r.state ? 'state' : aspectOf(r.values),\n values: r.values,\n where: where(r.hits),\n restyle: { ...(r.state ? { state: r.state } : {}), selector: r.selector },\n });\n }\n for (const [name, e] of foreign) {\n pieces.push({\n id: pieceId(['component', name]),\n kind: 'component',\n title: `New component: ${name}`,\n summary: `${name} comes from ${e.finding.library}, and the library has nothing like it.`,\n name,\n component: name.split('.').pop()!,\n values: [],\n where: where(e.hits),\n });\n }\n for (const t of tokens.values()) {\n const nearest = nearestOf(t.hits[0]!.finding.suggestion);\n const pname = t.name ?? `${t.category === 'color' ? 'custom' : t.category}`;\n const path = proposedTokenPath(nearest?.token, pname, t.category);\n const tokenFiles = nearest && input.set ? [...new Set(input.set.defs.filter((d) => d.path === nearest.token).map((d) => d.file))] : [];\n const n = t.hits.length;\n pieces.push({\n id: pieceId([t.category, t.name ?? t.value.replace(/^#/, '')]),\n kind: 'token',\n title: valueTitle(t.category, t.value),\n summary: `Written by hand in ${n === 1 ? 'one place' : `${n} places`}.${nearest ? ` ${nearest.level === 'high' ? 'It looks a lot like' : 'The closest in the library is'} ${nearest.label} (${nearest.value}).` : ' The library has nothing close.'}`,\n name: pname,\n values: [{ category: t.category, ...(t.hits[0]!.finding.property ? { property: t.hits[0]!.finding.property } : {}), value: t.value }],\n ...(nearest ? { nearest } : {}),\n where: where(t.hits),\n token: { path, type: TOKEN_TYPE[t.category] ?? 'dimension', value: t.value, files: tokenFiles },\n });\n }\n // Variants and components first (they read as features), then values; ids stay unique.\n const order: Record<MissingPiece['kind'], number> = { variant: 0, component: 1, token: 2 };\n const seen = new Set<string>();\n const out = pieces\n .sort((a, b) => order[a.kind] - order[b.kind])\n .map((p) => {\n let id = p.id;\n for (let i = 2; seen.has(id); i++) id = `${p.id}-${i}`;\n seen.add(id);\n return id === p.id ? p : { ...p, id };\n });\n return { pieces: out, fixes, files: files.length };\n}\n"],"names":["k","j","c","m","t","w","v","f","x","n","r","e","h","own","key","d","p"],"mappings":";;;AAqCA,MAAM,QAAQ,CAAC,SAAiB,KAAK,MAAM,OAAO;AAGlD,SAAS,QAAQ,MAA0C;AACzD,QAAM,0BAAU,IAAA;AAChB,aAAW,KAAK,OAAO,MAAM,IAAI,IAAI,CAAA,GAAI;AACvC,UAAMA,KAAI,EAAE,KAAA;AACZ,QAAIA,QAAO,IAAIA,KAAI,IAAI,IAAIA,EAAC,KAAK,KAAK,CAAC;AAAA,EACzC;AACA,SAAO;AACT;AAEA,MAAM,WAAW,CAAC,MAAc,MAAc,WAA2B;AACvE,MAAI,MAAM;AACV,QAAM,KAAK,KAAK,MAAM,IAAI;AAC1B,WAAS,IAAI,GAAG,IAAI,OAAO,KAAK,IAAI,GAAG,QAAQ,IAAK,QAAO,GAAG,CAAC,EAAG,SAAS;AAC3E,SAAO,MAAM,KAAK,IAAI,GAAG,SAAS,CAAC;AACrC;AAGO,SAAS,aAAa,MAAc,QAAwD;AACjG,WAAS,IAAI,SAAS,GAAG,KAAK,KAAK,IAAI,GAAG,SAAS,GAAI,GAAG,KAAK;AAC7D,QAAI,KAAK,CAAC,MAAM,OAAO,CAAC,WAAW,KAAK,KAAK,IAAI,CAAC,KAAK,EAAE,EAAG;AAC5D,QAAI,QAAQ;AACZ,QAAI,SAAS;AACb,aAASC,KAAI,IAAI,GAAGA,KAAI,QAAQA,MAAK;AACnC,YAAMC,KAAI,KAAKD,EAAC;AAChB,UAAIC,OAAM,IAAK;AAAA,eACNA,OAAM,IAAK;AAAA,eACXA,OAAM,OAAO,SAAS,KAAK,KAAKD,KAAI,CAAC,MAAM,KAAK;AACvD,iBAAS;AACT;AAAA,MACF;AAAA,IACF;AACA,QAAI,OAAQ,QAAO;AACnB,UAAME,KAAI,qBAAqB,KAAK,KAAK,MAAM,CAAC,CAAC;AACjD,WAAOA,KAAI,EAAE,MAAMA,GAAE,CAAC,GAAI,OAAO,MAAM;AAAA,EACzC;AACA,SAAO;AACT;AAGO,SAAS,UAAU,MAAc,UAAiC;AACvE,MAAI,QAAQ;AACZ,WAASF,KAAI,WAAW,GAAGA,KAAI,KAAK,QAAQA,MAAK;AAC/C,UAAMC,KAAI,KAAKD,EAAC;AAChB,QAAIC,OAAM,IAAK;AAAA,aACNA,OAAM,IAAK;AAAA,aACXA,OAAM,OAAO,SAAS,KAAK,KAAKD,KAAI,CAAC,MAAM,KAAK;AACvD,UAAI,KAAKA,KAAI,CAAC,MAAM,IAAK,QAAO;AAChC,YAAM,OAAO,aAAa,KAAK,KAAK,MAAMA,KAAI,CAAC,CAAC;AAChD,YAAMG,KAAI,OAAO,CAAC,GAAG,UAAU;AAC/B,aAAOA,MAAKA,GAAE,MAAM,KAAK,EAAE,UAAU,KAAKA,GAAE,UAAU,KAAKA,KAAI;AAAA,IACjE;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,YAAY,MAAc,QAA0B;AAClE,QAAM,OAAO,KAAK,YAAY,KAAK,MAAM;AACzC,MAAI,OAAO,EAAG,QAAO,CAAA;AACrB,QAAM,OAAO,KAAK,IAAI,KAAK,YAAY,KAAK,IAAI,GAAG,KAAK,YAAY,KAAK,IAAI,CAAC;AAC9E,QAAM,WAAW,KAAK,MAAM,OAAO,GAAG,IAAI;AAC1C,SAAO,CAAC,GAAG,SAAS,SAAS,sBAAsB,CAAC,EAAE,IAAI,CAACD,OAAMA,GAAE,CAAC,CAAE;AACxE;AAEA,MAAM,UAAU,oBAAI,IAAI,CAAC,UAAU,SAAS,OAAO,QAAQ,QAAQ,SAAS,UAAU,QAAQ,WAAW,aAAa,QAAQ,SAAS,QAAQ,SAAS,MAAM,MAAM,OAAO,WAAW,QAAQ,UAAU,KAAK,CAAC;AAGvM,SAAS,cAAc,KAAa,WAA4B;AACrE,QAAM,QAAQ,IACX,QAAQ,sBAAsB,OAAO,EACrC,MAAM,SAAS,EACf,IAAI,CAACE,OAAMA,GAAE,aAAa,EAC1B,OAAO,OAAO;AACjB,QAAM,OAAO,WAAW,YAAA;AACxB,QAAM,OAAO,MAAM,OAAO,CAACA,OAAMA,OAAM,QAAQ,CAAC,QAAQ,IAAIA,EAAC,CAAC;AAC9D,SAAO,KAAK,KAAK,SAAS,CAAC,KAAK,MAAM,MAAM,SAAS,CAAC,KAAK;AAC7D;AAEA,MAAM,SAAsC,EAAE,OAAO,QAAQ,QAAQ,SAAS,SAAS,QAAQ,MAAM,QAAQ,aAAa,QAAQ,eAAe,OAAA;AAEjJ,SAAS,SAAS,QAAmC;AACnD,aAAW,KAAK,CAAC,QAAQ,SAAS,MAAM,EAAY,KAAI,OAAO,KAAK,CAACC,OAAM,OAAOA,GAAE,QAAQ,MAAM,CAAC,EAAG,QAAO;AAC7G,SAAO;AACT;AAEA,MAAM,OAAO,CAACC,OAA4CA,GAAE,aAAa,UAAUA,GAAE,MAAM,YAAA,IAAgBA,GAAE,MAAM,QAAQ,QAAQ,EAAE;AAErI,SAAS,UAAU,GAA0D;AAC3E,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,EAAE,OAAO,EAAE,OAAO,QAAQ,EAAE,QAAQ,OAAO,EAAE,OAAO,OAAO,EAAE,OAAO,OAAO,WAAW,EAAE,KAAK,EAAA;AACtG;AAEA,MAAM,aAAqC,EAAE,OAAO,SAAS,UAAU,YAAY,eAAe,cAAc,eAAe,UAAU,WAAW,UAAU,eAAe,cAAc,QAAQ,UAAU,QAAQ,cAAA;AAErN,MAAM,aAAa,CAAC,UAAkB,UAA0B;AAC9D,QAAMD,KAAI,aAAa,WAAW,gBAAgB,KAAK,KAAK,IAAI,MAAM,YAAA,IAAgB;AACtF,QAAM,OAA+B,EAAE,OAAO,SAAS,QAAQ,UAAU,SAAS,WAAW,MAAM,QAAQ,aAAa,aAAa,eAAe,eAAe,eAAe,eAAe,QAAQ,UAAU,UAAU,YAAY,QAAQ,UAAU,WAAW,SAAS,gBAAgB,gBAAgB,eAAe,OAAA;AAC9T,SAAO,OAAO,KAAK,QAAQ,KAAK,QAAQ,IAAIA,EAAC;AAC/C;AAYA,MAAM,OAAO;AAEb,MAAM,iBAAqC;AAAA,EACzC,CAAC,UAAU,QAAQ;AAAA,EACnB,CAAC,2CAA2C,cAAc;AAAA,EAC1D,CAAC,6DAA6D,OAAO;AAAA,EACrE,CAAC,YAAY,SAAS;AAAA,EACtB,CAAC,uBAAuB,MAAM;AAAA,EAC9B,CAAC,eAAe,WAAW;AAAA,EAC3B,CAAC,iBAAiB,aAAa;AAAA,EAC/B,CAAC,iBAAiB,aAAa;AAAA,EAC/B,CAAC,UAAU,QAAQ;AACrB;AACA,MAAM,gBAAgB,CAAC,aAAqB,eAAe,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,KAAK,QAAQ,CAAC,IAAI,CAAC,KAAK;AAGrG,MAAM,cAAkC;AAAA,EACtC,CAAC,yBAAyB,SAAS;AAAA,EACnC,CAAC,0BAA0B,UAAU;AAAA,EACrC,CAAC,mCAAmC,SAAS;AAAA,EAC7C,CAAC,8DAA8D,MAAM;AAAA,EACrE,CAAC,qCAAqC,UAAU;AAAA,EAChD,CAAC,kDAAkD,SAAS;AAAA,EAC5D,CAAC,yBAAyB,SAAS;AAAA,EACnC,CAAC,8BAA8B,SAAS;AAAA,EACxC,CAAC,sBAAsB,aAAa;AAAA,EACpC,CAAC,6BAA6B,SAAS;AAAA,EACvC,CAAC,yDAAyD,SAAS;AAAA,EACnE,CAAC,0BAA0B,QAAQ;AACrC;AAGO,SAAS,cAAc,UAAsC;AAClE,QAAM,QAAkB,CAAA;AACxB,QAAM,MAAM,CAACD,OAAe,MAAM,SAASA,EAAC,IAAI,SAAY,MAAM,KAAKA,EAAC;AACxE,aAAWF,MAAK,SAAS,SAAS,4DAA4D,GAAG;AAC/F,UAAM,OAAOA,GAAE,CAAC,EAAG,YAAA;AACnB,UAAM,QAAQA,GAAE,CAAC,GAAG,YAAA;AACpB,QAAI,SAAS,WAAW,SAAS,QAAQ,SAAS,UAAU,SAAS,UAAU,SAAS,OAAQ;AAChG,QAAI,SAAS,gBAAgB,OAAO;AAClC,UAAI,KAAK;AACT;AAAA,IACF;AACA,UAAM,OAAO,YAAY,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,WAAW,OAAO,KAAK,KAAK,WAAW,OAAO,IAAI,KAAK,QAAQ,iBAAiB,EAAE,EAAE,QAAQ,MAAM,GAAG,IAAI;AAC9K,QAAI,KAAM,KAAI,UAAU,UAAU,OAAO,IAAI,KAAK,IAAI;AAAA,EACxD;AACA,aAAWA,MAAK,SAAS,SAAS,YAAY,GAAG;AAC/C,UAAM,OAAO,YAAY,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,KAAKA,GAAE,CAAC,EAAG,YAAA,CAAa,CAAC,IAAI,CAAC;AACzE,QAAI,UAAU,IAAI;AAAA,EACpB;AACA,SAAO,MAAM,SAAS,MAAM,KAAK,OAAO,IAAI;AAC9C;AAGO,SAAS,kBAAkB,UAA4B;AAC5D,QAAM,MAAgB,CAAA;AACtB,MAAI,MAAM;AACV,MAAI,QAAQ;AACZ,aAAWD,MAAK,UAAU;AACxB,QAAIA,OAAM,OAAOA,OAAM,IAAK;AAAA,aACnBA,OAAM,OAAOA,OAAM,IAAK;AACjC,QAAI,UAAU,KAAK,UAAU,KAAKA,EAAC,GAAG;AACpC,UAAI,IAAI,YAAY,KAAK,IAAI,MAAM;AACnC,YAAM;AACN;AAAA,IACF;AACA,WAAOA;AAAA,EACT;AACA,MAAI,IAAI,YAAY,KAAK,IAAI,MAAM;AACnC,SAAO;AACT;AAEA,MAAM,WAAW,CAAC,aAA+B;AAC/C,QAAM,MAAgB,CAAA;AACtB,MAAI,QAAQ;AACZ,MAAI,MAAM;AACV,aAAWA,MAAK,UAAU;AACxB,QAAIA,OAAM,OAAOA,OAAM,IAAK;AAAA,aACnBA,OAAM,OAAOA,OAAM,IAAK;AACjC,QAAIA,OAAM,OAAO,UAAU,GAAG;AAC5B,UAAI,KAAK,GAAG;AACZ,YAAM;AAAA,IACR,MAAO,QAAOA;AAAA,EAChB;AACA,MAAI,KAAK,GAAG;AACZ,SAAO,IAAI,IAAI,CAACM,OAAMA,GAAE,KAAA,CAAM,EAAE,OAAO,OAAO;AAChD;AAGA,SAAS,OAAO,UAAkD;AAChE,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,MAAM,kBAAkB,KAAK,QAAQ,IAAI,CAAC,GAAG,YAAA;AACnD,MAAI,OAAO,CAAC,OAAO,QAAQ,OAAO,KAAK,UAAU,KAAK,EAAE,SAAS,GAAG,EAAG,QAAO;AAC9E,QAAM,MAAM,sBAAsB,KAAK,QAAQ,IAAI,CAAC;AACpD,MAAI,IAAK,QAAO,cAAc,GAAG;AACjC,SAAO;AACT;AAYA,SAAS,YAAY,QAAuB,OAA4B;AACtE,QAAM,MAAM,QAAQ,MAAM;AAC1B,QAAM,0BAAU,IAAA;AAChB,QAAM,KAAK,EAAE,QAAQ,CAAC,GAAG,MAAM;AAC7B,UAAMR,KAAI,EAAE,KAAA;AACZ,QAAI,CAACA,GAAG;AACR,UAAMS,KAAI,IAAI,IAAIT,EAAC,KAAK;AACxB,QAAIS,KAAI,EAAG,KAAI,IAAIT,IAAGS,KAAI,CAAC;AAAA,QACtB,KAAI,IAAI,IAAI,CAAC;AAAA,EACpB,CAAC;AACD,SAAO;AACT;AAMO,SAAS,cAAc,OAA+G;AAC3I,QAAM,EAAE,OAAO;AACf,QAAM,QAAQ,MAAM,MAAM,OAAO,CAACF,OAAMA,GAAE,UAAU,QAAQ,YAAYA,GAAE,IAAI,CAAC;AAC/E,QAAM,QAAsC,CAAA;AAC5C,QAAM,+BAAe,IAAA;AACrB,QAAM,6BAAa,IAAA;AACnB,QAAM,8BAAc,IAAA;AAIpB,QAAM,oCAAoB,IAAA;AAC1B,QAAM,WAAW,CAAC,QAAmE;AACnF,QAAI,cAAc,IAAI,GAAG,EAAG,QAAO,cAAc,IAAI,GAAG;AACxD,UAAML,KAAI,IAAI,QAAQ,uBAAuB,MAAM;AACnD,UAAM,KAAK,IAAI,OAAO,0CAA0CA,EAAC,cAAcA,EAAC,uEAAuEA,EAAC,eAAe,GAAG;AAC1K,QAAI,QAA2D;AAC/D,eAAWK,MAAK,OAAO;AACrB,iBAAWJ,MAAKI,GAAE,MAAO,SAAS,EAAE,GAAG;AACrC,cAAM,OAAOJ,GAAE,CAAC,EAAG,MAAM,GAAG,EAAE,CAAC;AAC/B,YAAI,GAAG,WAAW,IAAI,IAAI,GAAG;AAC3B,kBAAQ,EAAE,WAAW,MAAM,MAAM,UAAUI,GAAE,OAAQJ,GAAE,KAAM,EAAA;AAC7D;AAAA,QACF;AAAA,MACF;AACA,UAAI,MAAO;AAAA,IACb;AACA,kBAAc,IAAI,KAAK,KAAK;AAC5B,WAAO;AAAA,EACT;AACA,QAAM,+BAAe,IAAA;AACrB,QAAM,aAAa,CAACO,IAAqC,OAAmB,OAAuC;AACjH,UAAM,MAAM,GAAGA,GAAE,SAAS,IAAIA,GAAE,SAAS,EAAE,IAAIA,GAAE,QAAQ,KAAKA,GAAE,IAAI;AACpE,UAAMC,KAAI,SAAS,IAAI,GAAG,KAAK,EAAE,GAAGD,IAAG,QAAQ,IAAI,MAAM,CAAA,EAAC;AAC1D,QAAI,CAACC,GAAE,OAAO,KAAK,CAACL,OAAMA,GAAE,aAAa,MAAM,YAAYA,GAAE,SAAS,MAAM,QAAQA,GAAE,UAAU,MAAM,KAAK,EAAG,CAAAK,GAAE,OAAO,KAAK,KAAK;AACjI,QAAI,CAACA,GAAE,KAAK,KAAK,CAACC,OAAMA,GAAE,SAAS,GAAG,QAAQA,GAAE,SAAS,GAAG,IAAI,EAAG,CAAAD,GAAE,KAAK,KAAK,EAAE;AACjF,aAAS,IAAI,KAAKA,EAAC;AAAA,EACrB;AAEA,QAAM,WAAW,CAAC,aAAuE;AACvF,eAAWR,MAAK,CAAC,GAAG,SAAS,SAAS,sBAAsB,CAAC,EAAE,WAAW;AACxE,YAAM,MAAMA,GAAE,CAAC;AACf,YAAMU,OAAM,CAAC,GAAG,GAAG,UAAU,EAAE,KAAK,CAACJ,OAAMA,GAAE,YAAA,MAAkB,IAAI,aAAa;AAChF,UAAII,KAAK,QAAO,EAAE,WAAWA,MAAK,KAAK,KAAA;AACvC,YAAM,MAAM,SAAS,GAAG;AACxB,UAAI,IAAK,QAAO,EAAE,WAAW,IAAI,WAAW,IAAA;AAAA,IAC9C;AACA,UAAM,OAAO,yCAAyC,KAAK,QAAQ,IAAI,CAAC;AACxE,UAAM,MAAM,OAAO,CAAC,GAAG,GAAG,UAAU,EAAE,KAAK,CAACJ,OAAMA,GAAE,YAAA,MAAkB,KAAK,YAAA,CAAa,IAAI;AAC5F,WAAO,MAAM,EAAE,WAAW,KAAK,KAAK,SAAS;AAAA,EAC/C;AAEA,aAAWF,MAAK,OAAO;AACrB,UAAM,QAAQA,GAAE;AAChB,UAAM,SAAS,UAAUA,GAAE,MAAM,OAAO,EAAE;AAC1C,UAAM,SAAS,QAAQA,GAAE,MAAM;AAC/B,UAAM,aAAa,MAAM,KAAK;AAE9B,UAAM,QAAQ,CAAC,SAAiB;AAC9B,YAAMP,KAAI,WAAW,OAAO,CAAC,GAAG,UAAU;AAC1C,YAAMS,KAAI,OAAO,IAAIT,EAAC,KAAK;AAC3B,UAAIS,KAAI,GAAG;AACT,eAAO,IAAIT,IAAGS,KAAI,CAAC;AACnB,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AACA,UAAM,iCAAiB,IAAA;AACvB,UAAM,QAAQ,CAAC,SAAiB;AAC9B,UAAI,CAAC,WAAW,IAAI,IAAI,cAAc,IAAI,MAAM,MAAM,IAAI,CAAC;AAC3D,aAAO,WAAW,IAAI,IAAI;AAAA,IAC5B;AACA,UAAM,MAAM,OAAO,SAAS,SAAS,OAAO,SAAS,UAAU,OAAO,SAAS;AAC/E,eAAW,WAAW,OAAO,UAAU;AACrC,UAAI,CAAC,MAAM,QAAQ,IAAI,EAAG;AAC1B,YAAM,IAAI,QAAQ;AAClB,YAAM,KAAK,EAAE,MAAMF,GAAE,MAAM,MAAM,QAAQ,KAAA;AACzC,UAAI,GAAG,UAAU,WAAY,GAAG,UAAU,UAAU,QAAQ,aAAa,SAAU;AACjF,cAAM,KAAK,EAAE,GAAG,IAAI,MAAM,QAAQ,OAAO,KAAK,EAAE,OAAO;AACvD;AAAA,MACF;AACA,YAAM,SAAS,SAAS,OAAO,QAAQ,MAAM,QAAQ,MAAM;AAC3D,UAAI,YAA2B;AAC/B,UAAI,OAAsB;AAC1B,UAAI,KAAK;AACP,cAAM,UAAU,YAAY,OAAO,MAAM;AACzC,mBAAW,OAAO,CAAC,GAAG,OAAO,EAAE,WAAW;AACxC,gBAAM,MAAM,SAAS,GAAG;AACxB,cAAI,KAAK;AACP,wBAAY,IAAI;AAChB,mBAAO,cAAc,KAAK,IAAI,SAAS;AACvC;AAAA,UACF;AAAA,QACF;AACA,YAAI,CAAC,aAAa,QAAQ,OAAQ,QAAO,cAAc,QAAQ,QAAQ,SAAS,CAAC,CAAE;AAAA,MACrF,OAAO;AACL,cAAM,MAAM,aAAa,OAAO,MAAM;AACtC,cAAM,OAAO,KAAK,KAAK,MAAM,GAAG,EAAE,CAAC;AACnC,YAAI,OAAO,QAAQ,GAAG,WAAW,IAAI,IAAI,GAAG;AAC1C,sBAAY;AACZ,iBAAO,UAAU,OAAO,IAAI,KAAK;AACjC,gBAAM,MAAM,8BAA8B,KAAK,MAAM,MAAM,IAAI,OAAO,MAAM,CAAC,IAAI,CAAC;AAClF,cAAI,CAAC,QAAQ,IAAK,QAAO,cAAc,KAAK,IAAI;AAAA,QAClD;AAAA,MACF;AACA,YAAM,MAAW,EAAE,GAAG,IAAI,QAAA;AAC1B,UAAI,WAAW;AACb,cAAM,SAAS,QAAQ,UAAU,YAAA;AACjC,cAAMO,OAAM,GAAG,SAAS,IAAI,KAAK;AACjC,cAAMR,KAAI,SAAS,IAAIQ,IAAG,KAAK,EAAE,WAAW,MAAM,OAAO,MAAM,GAAC;AAChE,QAAAR,GAAE,KAAK,KAAK,GAAG;AACf,iBAAS,IAAIQ,MAAKR,EAAC;AACnB;AAAA,MACF;AACA,YAAM,MAAM,GAAG,QAAQ,QAAQ,IAAI,KAAK,OAAO,CAAC;AAChD,YAAMF,KAAI,OAAO,IAAI,GAAG,KAAK,EAAE,UAAU,QAAQ,UAAU,OAAO,QAAQ,OAAO,MAAM,MAAM,CAAA,EAAC;AAC9F,UAAI,CAACA,GAAE,QAAQ,SAAQ,OAAO;AAC9B,MAAAA,GAAE,KAAK,KAAK,GAAG;AACf,aAAO,IAAI,KAAKA,EAAC;AAAA,IACnB;AAGA,UAAM,UAAU,IAAI,IAAI,OAAO,SAAS,IAAI,CAACI,OAAMA,GAAE,IAAI,CAAC;AAC1D,UAAM,QAAQ,YAAYD,GAAE,QAAQ,KAAK;AACzC,UAAM,QAAQ,QAAQ,KAAK;AAC3B,QAAI,KAAK;AACP,YAAM,QAAQ,cAAc,OAAO,OAAO,SAAS,KAAK;AACxD,iBAAWQ,MAAK,YAAY,OAAO,OAAO,IAA+B,EAAE,OAAO;AAChF,cAAM,WAAWA,GAAE,SAAS,YAAA;AAC5B,cAAM,EAAE,KAAA,IAAS,MAAMA,GAAE,UAAU;AACnC,YAAI,CAAC,KAAK,KAAK,QAAQ,KAAK,QAAQ,IAAI,IAAI,KAAK,CAAC,MAAM,IAAI,IAAI,EAAG;AACnE,cAAM,OAAO,MAAM,YAAY,KAAKA,GAAE,UAAU;AAChD,YAAI,OAAO,EAAG;AACd,cAAM,OAAO,KAAK,IAAI,MAAM,YAAY,KAAK,OAAO,CAAC,GAAG,MAAM,YAAY,KAAK,OAAO,CAAC,GAAG,MAAM,YAAY,KAAK,OAAO,CAAC,CAAC;AAC1H,cAAM,WAAW,MAAM,MAAM,OAAO,GAAG,IAAI,EAAE,KAAA,EAAO,QAAQ,QAAQ,GAAG;AACvE,YAAI,CAAC,YAAY,SAAS,WAAW,GAAG,EAAG;AAC3C,mBAAW,OAAO,SAAS,QAAQ,GAAG;AACpC,gBAAM,QAAQ,kBAAkB,GAAG;AACnC,cAAI,KAAK;AACT,cAAI,SAAsC;AAC1C,mBAAS,IAAI,MAAM,SAAS,GAAG,KAAK,KAAK,CAAC,QAAQ,KAAK;AACrD,qBAAS,SAAS,MAAM,CAAC,CAAE;AAC3B,gBAAI,OAAQ,MAAK;AAAA,UACnB;AACA,cAAI,CAAC,OAAQ;AACb,gBAAM,QAAQ,cAAc,MAAM,EAAE,CAAE;AACtC,gBAAM,OAAO,KAAK,MAAM,SAAS,IAAI,OAAO,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;AACvE,gBAAM,OAAO,OAAO,MAAM,cAAc,OAAO,KAAK,OAAO,SAAS,IAAI;AACxE,qBAAW,EAAE,WAAW,OAAO,WAAW,OAAO,MAAM,UAAU,IAAA,GAAO,EAAE,UAAU,cAAc,QAAQ,GAAG,UAAU,OAAOA,GAAE,MAAM,KAAA,GAAQ,GAAI,OAAO,EAAE,KAAA,IAAS,CAAA,EAAC,GAAM,EAAE,MAAMR,GAAE,MAAM,MAAM;AACjM;AAAA,QACF;AAAA,MACF;AAAA,IACF,OAAO;AAGL,iBAAWJ,MAAK,MAAM,SAAS,mBAAmB,GAAG;AACnD,cAAM,OAAOA,GAAE,CAAC,EAAG,MAAM,GAAG,EAAE,CAAC;AAC/B,YAAI,CAAC,GAAG,WAAW,IAAI,IAAI,EAAG;AAC9B,cAAM,WAAWA,GAAE;AACnB,YAAI,MAAM;AACV,YAAI,QAAQ;AACZ,iBAASF,KAAI,WAAW,GAAGA,KAAI,MAAM,QAAQA,MAAK;AAChD,gBAAMC,KAAI,MAAMD,EAAC;AACjB,cAAIC,OAAM,IAAK;AAAA,mBACNA,OAAM,IAAK;AAAA,mBACXA,OAAM,OAAO,SAAS,KAAK,MAAMD,KAAI,CAAC,MAAM,KAAK;AACxD,kBAAMA;AACN;AAAA,UACF;AAAA,QACF;AACA,YAAI,MAAM,EAAG;AACb,cAAM,MAAM,MAAM,MAAM,UAAU,GAAG;AACrC,cAAM,KAAK,aAAa,KAAK,GAAG;AAChC,YAAI,CAAC,GAAI;AACT,YAAI,KAAK;AACT,YAAI,QAAQ;AACZ,iBAASA,KAAI,GAAG,QAAQ,GAAG,CAAC,EAAE,SAAS,GAAGA,KAAI,IAAI,QAAQA,MAAK;AAC7D,cAAI,IAAIA,EAAC,MAAM,IAAK;AAAA,mBACX,IAAIA,EAAC,MAAM,OAAO,EAAE,OAAO,GAAG;AACrC,oBAAQA;AACR;AAAA,UACF;AAAA,QACF;AACA,YAAI,QAAQ,EAAG;AACf,cAAM,OAAO,IAAI,MAAM,GAAG,QAAQ,GAAG,CAAC,EAAE,QAAQ,KAAK;AACrD,cAAM,OAAO,gCAAgC,KAAK,IAAI,IAAI,CAAC;AAC3D,cAAM,OAAO,OAAO,IAAI,OAAO,6EAA6E,KAAK,QAAQ,OAAO,KAAK,CAAC,SAAS,EAAE,KAAK,GAAG,IAAI,CAAC,IAAI;AAClK,cAAM,QAAQ;AACd,cAAM,MAAM,8BAA8B,KAAK,GAAG,IAAI,CAAC;AACvD,cAAM,OAAO,UAAU,OAAO,QAAQ,GAAG,kBAAkB,MAAM,cAAc,KAAK,IAAI,IAAI;AAC5F,mBAAW,MAAM,KAAK,SAAS,6DAA6D,GAAG;AAC7F,gBAAM,WAAW,GAAG,CAAC,EAAG,QAAQ,UAAU,CAAC,OAAO,IAAI,GAAG,YAAA,CAAa,EAAE;AACxE,gBAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,CAAC,EAAE,SAAS,GAAG;AACpD,gBAAM,EAAE,SAAS,MAAM,MAAM,GAAG,CAAC,EAAE,SAAS,CAAC;AAC7C,cAAI,CAAC,KAAK,KAAK,QAAQ,KAAK,QAAQ,IAAI,IAAI,KAAK,CAAC,MAAM,IAAI,IAAI,EAAG;AACnE,qBAAW,EAAE,WAAW,MAAM,OAAO,MAAM,UAAU,GAAG,IAAI,SAAA,GAAY,EAAE,UAAU,cAAc,QAAQ,GAAG,UAAU,OAAO,GAAG,CAAC,EAAG,OAAK,GAAK,EAAE,MAAMM,GAAE,MAAM,KAAA,CAAM;AAAA,QACvK;AAAA,MACF;AAAA,IACF;AACA,eAAWL,MAAK,OAAO,YAAY;AACjC,UAAI,CAAC,MAAMA,GAAE,IAAI,EAAG;AACpB,UAAIA,GAAE,YAAY;AAChB,cAAM,KAAK,EAAE,MAAMK,GAAE,MAAM,MAAML,GAAE,MAAM,MAAMA,GAAE,MAAM,KAAKA,GAAE,YAAY;AAC1E;AAAA,MACF;AACA,UAAIA,GAAE,SAAS,UAAW;AAC1B,YAAMS,KAAI,QAAQ,IAAIT,GAAE,IAAI,KAAK,EAAE,SAASA,IAAG,MAAM,GAAC;AACtD,MAAAS,GAAE,KAAK,KAAK,EAAE,MAAMJ,GAAE,MAAM,MAAML,GAAE,MAAM;AAC1C,cAAQ,IAAIA,GAAE,MAAMS,EAAC;AAAA,IACvB;AAAA,EACF;AAEA,QAAM,QAAQ,CAAC,SAA2C,KAAK,MAAM,GAAG,CAAC,EAAE,IAAI,CAACC,QAAO,EAAE,MAAMA,GAAE,MAAM,MAAMA,GAAE,OAAO;AACtH,QAAM,SAAyB,CAAA;AAC/B,aAAWN,MAAK,SAAS,UAAU;AACjC,UAAM,SAAuB,CAAA;AAC7B,eAAWM,MAAKN,GAAE,MAAM;AACtB,YAAM,KAAK,EAAE,UAAUM,GAAE,QAAQ,UAAU,GAAIA,GAAE,QAAQ,WAAW,EAAE,UAAUA,GAAE,QAAQ,SAAA,IAAa,CAAA,GAAK,OAAOA,GAAE,QAAQ,MAAA;AAC7H,UAAI,CAAC,OAAO,KAAK,CAACJ,OAAMA,GAAE,aAAa,GAAG,YAAYA,GAAE,aAAa,GAAG,YAAY,KAAKA,EAAC,MAAM,KAAK,EAAE,CAAC,EAAG,QAAO,KAAK,EAAE;AAAA,IAC3H;AACA,UAAM,SAAS,SAAS,MAAM;AAC9B,UAAM,KAAKF,GAAE,KAAK,KAAK,CAACM,OAAM,aAAa,KAAKA,GAAE,QAAQ,YAAY,EAAE,CAAC,GAAG,QAAQ;AACpF,UAAM,UAAU,UAAU,MAAMN,GAAE,KAAK,KAAK,CAACM,OAAMA,GAAE,QAAQ,UAAU,GAAG,QAAQ,UAAU;AAC5F,UAAM,QAAQ,OAAO,IAAI,CAACJ,OAAM,GAAGA,GAAE,YAAYA,GAAE,QAAQ,IAAIA,GAAE,KAAK,EAAE,EAAE,KAAK,IAAI;AACnF,WAAO,KAAK;AAAA,MACV,IAAI,QAAQ,CAACF,GAAE,WAAWA,GAAE,MAAM,MAAM,CAAC;AAAA,MACzC,MAAM;AAAA,MACN,OAAO,GAAGA,GAAE,SAAS,KAAKA,GAAE,IAAI,IAAI,MAAM;AAAA,MAC1C,SAAS,GAAGA,GAAE,SAAS,sBAAsB,KAAK,KAAK,UAAU,IAAI,QAAQ,UAAU,SAAS,wBAAwB,+BAA+B,IAAI,QAAQ,KAAK,MAAM,EAAE;AAAA,MAChL,MAAMA,GAAE;AAAA,MACR,WAAWA,GAAE;AAAA,MACb;AAAA,MACA;AAAA,MACA,GAAI,UAAU,EAAE,QAAA,IAAY,CAAA;AAAA,MAC5B,OAAO,MAAMA,GAAE,IAAI;AAAA,IAAA,CACpB;AAAA,EACH;AACA,aAAWI,MAAK,SAAS,UAAU;AACjC,UAAM,QAAQA,GAAE,OAAO,IAAI,CAACF,OAAM,GAAGA,GAAE,OAAO,GAAGA,GAAE,IAAI,MAAM,EAAE,GAAGA,GAAE,YAAYA,GAAE,QAAQ,IAAIA,GAAE,KAAK,EAAE,EAAE,KAAK,IAAI;AAClH,UAAM,QAAQE,GAAE,QAAQ,GAAGA,GAAE,SAAS,mBAAmBA,GAAE,KAAK,KAAK,GAAGA,GAAE,SAAS,KAAKA,GAAE,SAAS,aAAa,aAAa,GAAGA,GAAE,IAAI,UAAU;AAChJ,WAAO,KAAK;AAAA,MACV,IAAI,QAAQ,CAACA,GAAE,WAAWA,GAAE,QAAQ,GAAGA,GAAE,KAAK,aAAaA,GAAE,SAAS,aAAa,YAAY,GAAGA,GAAE,IAAI,UAAU,CAAC;AAAA,MACnH,MAAM;AAAA,MACN;AAAA,MACA,SAAS,GAAGA,GAAE,SAAS,qCAAqCA,GAAE,QAAQ,SAASA,GAAE,KAAK,KAAK,EAAE,kBAAkB,KAAK,kBAAkBA,GAAE,SAAS,WAAWA,GAAE,QAAQ,wBAAwB,cAAc;AAAA,MAC5M,MAAMA,GAAE,SAASA,GAAE;AAAA,MACnB,WAAWA,GAAE;AAAA,MACb,QAAQA,GAAE,QAAQ,UAAU,SAASA,GAAE,MAAM;AAAA,MAC7C,QAAQA,GAAE;AAAA,MACV,OAAO,MAAMA,GAAE,IAAI;AAAA,MACnB,SAAS,EAAE,GAAIA,GAAE,QAAQ,EAAE,OAAOA,GAAE,UAAU,CAAA,GAAK,UAAUA,GAAE,SAAA;AAAA,IAAS,CACzE;AAAA,EACH;AACA,aAAW,CAAC,MAAMC,EAAC,KAAK,SAAS;AAC/B,WAAO,KAAK;AAAA,MACV,IAAI,QAAQ,CAAC,aAAa,IAAI,CAAC;AAAA,MAC/B,MAAM;AAAA,MACN,OAAO,kBAAkB,IAAI;AAAA,MAC7B,SAAS,GAAG,IAAI,eAAeA,GAAE,QAAQ,OAAO;AAAA,MAChD;AAAA,MACA,WAAW,KAAK,MAAM,GAAG,EAAE,IAAA;AAAA,MAC3B,QAAQ,CAAA;AAAA,MACR,OAAO,MAAMA,GAAE,IAAI;AAAA,IAAA,CACpB;AAAA,EACH;AACA,aAAWP,MAAK,OAAO,UAAU;AAC/B,UAAM,UAAU,UAAUA,GAAE,KAAK,CAAC,EAAG,QAAQ,UAAU;AACvD,UAAM,QAAQA,GAAE,QAAQ,GAAGA,GAAE,aAAa,UAAU,WAAWA,GAAE,QAAQ;AACzE,UAAM,OAAO,kBAAkB,SAAS,OAAO,OAAOA,GAAE,QAAQ;AAChE,UAAM,aAAa,WAAW,MAAM,MAAM,CAAC,GAAG,IAAI,IAAI,MAAM,IAAI,KAAK,OAAO,CAACW,OAAMA,GAAE,SAAS,QAAQ,KAAK,EAAE,IAAI,CAACA,OAAMA,GAAE,IAAI,CAAC,CAAC,IAAI,CAAA;AACpI,UAAMN,KAAIL,GAAE,KAAK;AACjB,WAAO,KAAK;AAAA,MACV,IAAI,QAAQ,CAACA,GAAE,UAAUA,GAAE,QAAQA,GAAE,MAAM,QAAQ,MAAM,EAAE,CAAC,CAAC;AAAA,MAC7D,MAAM;AAAA,MACN,OAAO,WAAWA,GAAE,UAAUA,GAAE,KAAK;AAAA,MACrC,SAAS,sBAAsBK,OAAM,IAAI,cAAc,GAAGA,EAAC,SAAS,IAAI,UAAU,IAAI,QAAQ,UAAU,SAAS,wBAAwB,+BAA+B,IAAI,QAAQ,KAAK,KAAK,QAAQ,KAAK,OAAO,iCAAiC;AAAA,MACnP,MAAM;AAAA,MACN,QAAQ,CAAC,EAAE,UAAUL,GAAE,UAAU,GAAIA,GAAE,KAAK,CAAC,EAAG,QAAQ,WAAW,EAAE,UAAUA,GAAE,KAAK,CAAC,EAAG,QAAQ,SAAA,IAAa,IAAK,OAAOA,GAAE,MAAA,CAAO;AAAA,MACpI,GAAI,UAAU,EAAE,QAAA,IAAY,CAAA;AAAA,MAC5B,OAAO,MAAMA,GAAE,IAAI;AAAA,MACnB,OAAO,EAAE,MAAM,MAAM,WAAWA,GAAE,QAAQ,KAAK,aAAa,OAAOA,GAAE,OAAO,OAAO,WAAA;AAAA,IAAW,CAC/F;AAAA,EACH;AAEA,QAAM,QAA8C,EAAE,SAAS,GAAG,WAAW,GAAG,OAAO,EAAA;AACvF,QAAM,2BAAW,IAAA;AACjB,QAAM,MAAM,OACT,KAAK,CAAC,GAAG,MAAM,MAAM,EAAE,IAAI,IAAI,MAAM,EAAE,IAAI,CAAC,EAC5C,IAAI,CAACY,OAAM;AACV,QAAI,KAAKA,GAAE;AACX,aAAS,IAAI,GAAG,KAAK,IAAI,EAAE,GAAG,IAAK,MAAK,GAAGA,GAAE,EAAE,IAAI,CAAC;AACpD,SAAK,IAAI,EAAE;AACX,WAAO,OAAOA,GAAE,KAAKA,KAAI,EAAE,GAAGA,IAAG,GAAA;AAAA,EACnC,CAAC;AACH,SAAO,EAAE,QAAQ,KAAK,OAAO,OAAO,MAAM,OAAA;AAC5C;"}
@@ -32,6 +32,10 @@ export declare function childText(text: string, tagStart: number): string | null
32
32
  export declare function ruleClasses(text: string, offset: number): string[];
33
33
  /** "badgeOverdue" on Badge -> "overdue"; "invoice-status--late" -> "late". */
34
34
  export declare function nameFromClass(cls: string, component?: string): string;
35
+ /** The state a compound selector names: `[aria-pressed='true']:hover` gives "pressed and hovered". */
36
+ export declare function selectorState(compound: string): string | undefined;
37
+ /** A selector split into its compound parts: `.a[x] > svg` gives [".a[x]", "svg"]. */
38
+ export declare function selectorCompounds(selector: string): string[];
35
39
  /**
36
40
  * The pieces a product branch adds that the library lacks. `files` are the changed files (only auditable ones are
37
41
  * read; pass the product's files). `set` lets token pieces name the token files of the closest token (light and dark).
@@ -334,6 +334,19 @@ export declare function hashSpec(spec: Omit<ComponentSpec, 'hash'> | ComponentSp
334
334
  export declare const variantName: (props: Record<string, string>) => string;
335
335
  /** Variant key independent of axis order (same as the plugin's signature keys): sorted "Size=md, Variant=primary". */
336
336
  export declare const variantKeyOf: (props: Record<string, string>) => string;
337
+ /**
338
+ * The set's variant a report key names. A Check fidelity key carries only the axes that vary between the shots
339
+ * ("Size=sm, State=Default, Variant=primary"), while the set may have more ("Loading", "Full width"). The exact key
340
+ * wins; otherwise the variants whose props hold every axis and value asked, and among several the one whose other axes
341
+ * sit at their default (the axis default, else "false" or "Default"). Undefined when none, or when that still leaves
342
+ * more than one: a guess would compare the wrong variant.
343
+ */
344
+ export declare function matchVariant<V extends {
345
+ props: Record<string, string>;
346
+ }>(spec: {
347
+ axes?: SpecAxis[] | undefined;
348
+ variants: V[];
349
+ } | undefined, props: Record<string, string>): V | undefined;
337
350
  /** What a set's description says about reduced motion: tokens set every duration to 0 under prefers-reduced-motion. */
338
351
  export declare const REDUCED_MOTION_NOTE = "In code, reduced motion turns this off.";
339
352
  /** One prototype reaction: on `trigger`, the variant `fromKey` changes to `toKey`, with the code's transition. */